Javascript 使用节点js在foor循环中同步多个请求
我是从Javascript开始的,我需要帮助来弄清楚如何在循环for循环时使代码同步。 基本上,我所做的是在for循环中发出多个POST请求,然后使用库删除数据,最后将结果保存到Mongo数据库。 输出正常,但它以无序方式出现,并突然挂起,我必须使用ctrl+C强制关闭。这是我的功能:Javascript 使用节点js在foor循环中同步多个请求,javascript,node.js,web-scraping,request,x-ray,Javascript,Node.js,Web Scraping,Request,X Ray,我是从Javascript开始的,我需要帮助来弄清楚如何在循环for循环时使代码同步。 基本上,我所做的是在for循环中发出多个POST请求,然后使用库删除数据,最后将结果保存到Mongo数据库。 输出正常,但它以无序方式出现,并突然挂起,我必须使用ctrl+C强制关闭。这是我的功能: function getdata() { const startYear = 1996; const currentYear = 1998; // new Date().getFullYear()
function getdata() {
const startYear = 1996;
const currentYear = 1998; // new Date().getFullYear()
for (let i = startYear; i <= currentYear; i++) {
for (let j = 1; j <= 12; j++) {
if (i === startYear) {
j = 12;
}
// Form to be sent
const form = {
year: `${i}`,
month: `${j}`,
day: '01',
};
const formData = querystring.stringify(form);
const contentLength = formData.length;
// Make HTTP Request
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded',
},
uri: 'https://www.ipma.pt/pt/geofisica/sismologia/',
body: formData,
method: 'POST',
}, (err, res, html) => {
if (!err && res.statusCode === 200) {
// Scrapping data with X-Ray
x(html, '#divID0 > table > tr', {
date: '.block90w',
lat: 'td:nth-child(2)',
lon: 'td:nth-child(3)',
prof: 'td:nth-child(4)',
mag: 'td:nth-child(5)',
local: 'td:nth-child(6)',
degree: 'td:nth-child(7)',
})((error, obj) => {
const result = {
date: obj.date,
lat: obj.lat.replace(',', '.'),
lon: obj.lon.replace(',', '.'),
prof: obj.prof == '-' ? null : obj.prof.replace(',', '.'),
mag: obj.mag.replace(',', '.'),
local: obj.local,
degree: obj.degree,
};
// console.log(result);
upsertEarthquake(result); // save to DB
});
}
});
}
}
}
函数getdata(){
常数startYear=1996;
const currentYear=1998;//新日期().getFullYear()
for(让i=startYear;i{
常数结果={
日期:obj.date,
横向:对象横向替换(',','),
lon:obj.lon.replace(',','),
prof:obj.prof=='-'?null:obj.prof.replace(',','.'),
mag:obj.mag.replace(',','),
本地:obj.local,
学位:obj学位,
};
//控制台日志(结果);
Upsert地震(结果);//保存到数据库
});
}
});
}
}
}
我想我必须使用承诺或回调,但我不知道如何做到这一点,我已经尝试使用async await,但没有成功。如果需要提供任何其他信息,请告诉我,谢谢。您正在循环中调用请求 异步函数是在主线程逻辑结束后获取结果(即在回调函数中接收响应)的函数 这样,如果我们有:
for (var i = 0; i < 12; i++) {
request({
data: i
}, function(error, data) {
// This is the request result, inside a callback function
});
}
在这里你可以看到逻辑。您有一个名为callNext
的函数,如果不需要更多调用,它将进行下一次调用或调用requestend
当在
callNext
内部调用request
时,它将等待接收回调(这将在将来某个时候异步发生),将处理收到的数据,然后在回调中告诉他再次调用callNext
而不是循环。您可以使用开始和结束年份创建一个数组,然后将该数组映射到您请求的配置,然后将结果映射到x射线返回的值(x射线返回一个数组,因此无需回调)。然后使用返回承诺的函数将刮取结果放入mongodb中
如果某个对象被拒绝,则创建一个Fail
类型的对象,并使用该对象进行解析
使用Promise并行启动所有请求、x射线和mongo。使用Promise限制活动请求的数量
下面是代码中的内容:
//you can get library containing throttle here:
// https://github.com/amsterdamharu/lib/blob/master/src/index.js
const lib = require('lib');
const Fail = function(details){this.details=details;};
const isFail = o=>(o&&o.constructor)===Fail;
const max10 = lib.throttle(10);
const range = lib.range;
const createYearMonth = (startYear,endYear)=>
range(startYear,endYear)
.reduce(
(acc,year)=>
acc.concat(
range(1,12).map(month=>({year,month}))
)
,[]
);
const toRequestConfigs = yearMonths =>
yearMonths.map(
yearMonth=>{
const formData = querystring.stringify(yearMonth);
return {
headers: {
'Content-Length': formData.length,
'Content-Type': 'application/x-www-form-urlencoded',
},
uri: 'https://www.ipma.pt/pt/geofisica/sismologia/',
body: formData,
method: 'POST',
};
}
);
const scrape = html =>
x(
html,
'#divID0 > table > tr',
{
date: '.block90w',
lat: 'td:nth-child(2)',
lon: 'td:nth-child(3)',
prof: 'td:nth-child(4)',
mag: 'td:nth-child(5)',
local: 'td:nth-child(6)',
degree: 'td:nth-child(7)'
}
);
const requestAsPromise = config =>
new Promise(
(resolve,reject)=>
request(
config,
(err,res,html)=>
(!err && res.statusCode === 200)
//x-ray returns a promise:
// https://github.com/matthewmueller/x-ray#xraythencb
? resolve(html)
: reject(err)
)
);
const someMongoStuff = scrapeResult =>
//do mongo stuff and return promise
scrapeResult;
const getData = (startYear,endYear) =>
Promise.all(
toRequestConfigs(
createYearMonth(startYear,endYear)
)
.map(
config=>
//maximum 10 active requests
max10(requestAsPromise)(config)
.then(scrape)
.then(someMongoStuff)
.catch(//if something goes wrong create a Fail type object
err => new Fail([err,config.body])
)
)
)
//how to use:
getData(1980,1982)
.then(//will always resolve unless toRequestConfigs or createYearMonth throws
result=>{
//items that were successfull
const successes = result.filter(item=>!isFail(item));
//items that failed
const failed = result.filter(isFail);
}
)
抓取经常发生的情况是,目标站点在y周期内不允许您发出超过x个请求,并且开始将您的IP列入黑名单,如果您超过此期限,则拒绝服务
假设您希望限制为每5秒10个请求,那么您可以将上述代码更改为:
const max10 = lib.throttlePeriod(10,5000);
代码的其余部分是相同的您有
sync for…循环
,其中有异步方法
解决这一问题的干净方法是使用
ES2017async/await
语法
假设您希望在upsertSevention(result)
之后停止每个迭代,您应该更改代码以获得类似的结果
function async getdata() {
const startYear = 1996;
const currentYear = 1998; // new Date().getFullYear()
for (let i = startYear; i <= currentYear; i++) {
for (let j = 1; j <= 12; j++) {
if (i === startYear)
j = 12;
// Form to be sent
const form = {
year: `${i}`,
month: `${j}`,
day: '01',
};
const formData = querystring.stringify(form);
const contentLength = formData.length;
//Make HTTP Request
await new Promise((next, reject)=> {
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded',
},
uri: 'https://www.ipma.pt/pt/geofisica/sismologia/',
body: formData,
method: 'POST',
}, (err, res, html) => {
if (err || res.statusCode !== 200)
return next() //If there is an error jump to the next
//Scrapping data with X-Ray
x(html, '#divID0 > table > tr', {
date: '.block90w',
lat: 'td:nth-child(2)',
lon: 'td:nth-child(3)',
prof: 'td:nth-child(4)',
mag: 'td:nth-child(5)',
local: 'td:nth-child(6)',
degree: 'td:nth-child(7)',
})((error, obj) => {
const result = {
date: obj.date,
lat: obj.lat.replace(',', '.'),
lon: obj.lon.replace(',', '.'),
prof: obj.prof == '-' ? null : obj.prof.replace(',', '.'),
mag: obj.mag.replace(',', '.'),
local: obj.local,
degree: obj.degree,
}
//console.log(result);
upsertEarthquake(result); // save to DB
next() //This makes jump to the next for... iteration
})
})
}
}
}
}
我试图实现它,但是输出仍然是无序的,尽管它不再挂起。你可以在这里看到我所做的@MiguelFerreira你明白了。有了这些信息,您创建了一种连接异步回调的方法,这很酷,但我仍然看到了一个问题。您在另一个异步请求中有一个异步请求。在调用
x
的请求
回调中,该回调也有一个异步回调。您希望在所有异步回调完成后执行下一个请求。我对你的粘贴库做了一些调整:在你得到你想要的结果之后,检查我调用getdata
的地方。此时,您已完成并可以继续执行下一个请求。请注意,如果出现错误或其他原因导致您不调用x
函数,它将无法继续执行下一个请求。想想这是不是你真正想要的东西。也许你想重复这个请求,或者在这种情况下从头再开始。这取决于你。只需添加一个else
,并在“请求链”中断时添加您想要运行的代码。感谢jorge花费时间和精力,您的解决方案非常简单,而且非常适合您+1!编辑:我不能投票,因为我还有@MiguelFerreira哈哈,没问题。很高兴它成功了:-)非常感谢您的时间和努力,但jorge回答得更快,而且解决方案更简单。@MiguelFerreira没问题,如果您需要帮助实现此解决方案,因为您了解节流并行与刮取的关系,或者如果您因为一个失败而丢失所有成功处理的项,或者如果您了解命令式编程和声明式编程之间的区别,请告诉我。
function async getdata() {
const startYear = 1996;
const currentYear = 1998; // new Date().getFullYear()
for (let i = startYear; i <= currentYear; i++) {
for (let j = 1; j <= 12; j++) {
if (i === startYear)
j = 12;
// Form to be sent
const form = {
year: `${i}`,
month: `${j}`,
day: '01',
};
const formData = querystring.stringify(form);
const contentLength = formData.length;
//Make HTTP Request
await new Promise((next, reject)=> {
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded',
},
uri: 'https://www.ipma.pt/pt/geofisica/sismologia/',
body: formData,
method: 'POST',
}, (err, res, html) => {
if (err || res.statusCode !== 200)
return next() //If there is an error jump to the next
//Scrapping data with X-Ray
x(html, '#divID0 > table > tr', {
date: '.block90w',
lat: 'td:nth-child(2)',
lon: 'td:nth-child(3)',
prof: 'td:nth-child(4)',
mag: 'td:nth-child(5)',
local: 'td:nth-child(6)',
degree: 'td:nth-child(7)',
})((error, obj) => {
const result = {
date: obj.date,
lat: obj.lat.replace(',', '.'),
lon: obj.lon.replace(',', '.'),
prof: obj.prof == '-' ? null : obj.prof.replace(',', '.'),
mag: obj.mag.replace(',', '.'),
local: obj.local,
degree: obj.degree,
}
//console.log(result);
upsertEarthquake(result); // save to DB
next() //This makes jump to the next for... iteration
})
})
}
}
}
}
if (err || res.statusCode !== 200)
return reject(err)