javascript - Promisify 使用速率限制器的回调
问题描述
我正在做一个项目,我必须遍历几个 api 并在每个上下载多个页面。我的代码使用回调工作,但我不确定如何在最后一个 http 请求完成后运行代码。
const RateLimiter = require('limiter').RateLimiter;
const limiter = new RateLimiter(1, 'second');
request(url,{json:true}function(err,res,body){
let bunchOfURLs = body['bunchOfURLs'];
for(let i = 0; i < bunchOfURLs.length;i++){
limiter.removeTokens(1,function(){
request(bunchOfURLs[i],{json:true},function(err2,res2,body2){
let moreURLs = body2['moreURLs'];
for(let j = 0; j < moreURLs.length;j++){
request(moreURLs[j],function(err3,res3,body3){
// Once all downloads are done console.log('Done!')
});
}
});
}
}
});
我认为正确的做法是将所有函数嵌套在 Promise 中并Promise.all
在每个函数上使用。问题在于速率限制器,我认为在调用之前不会将嵌套承诺添加到promises
数组Promise.all
中。
const RateLimiter = require('limiter').RateLimiter;
const limiter = new RateLimiter(1, 'second');
request(url,{json:true}function(err,res,body){
let bunchOfURLs = body['bunchOfURLs'];
let promises = [];
for(let i = 0; i < bunchOfURLs.length;i++){
promises.push(new Promise((resolve,reject)=>{
limiter.removeTokens(1,function(){
request(bunchOfURLs[i],{json:true},function(err2,res2,body2){
let moreURLs = body2['moreURLs'];
for(let j = 0; j < moreURLs.length;j++){
// I dont' think these will be added
promises.push(new Promise((resolve,reject)=>{
request(moreURLs[j],function(err3,res3,body3){
resolve();
});
}))
}
});
}
}))
}
Promise.all(promises).then(()=>{console.log('Done!')});
});
解决方案
如果您打算使用Promises
该limiter
模块,那么您可能需要创建一个简单的包装方法来删除令牌。使用该模块也很理想request-promise
,或者也可以创建另一个简单的包装器来发出请求。
看起来像这样:
function removeTokens(tokens) {
return new Promise((resolve) => {
limiter.removeTokens(tokens, resolve);
});
}
request(url, {json: true}).then((body) => {
let promises = body['bunchOfURLs'].map((url2) => {
return removeTokens(1).then(() => {
return request(url2, {json:true}).then((body2) => {
let promises2 = body2['moreURLs'].map((url3) => {
return request(url3);
});
return Promise.all(promises2);
});
});
});
return Promise.all(promise);
});
如果您选择创建一个简单的包装器而不是request-promise
:
function requestJSON(url) {
return Promise((resolve, reject) => {
request(url, {json: true}, (err, res, body) => {
if (err) {
reject(err);
} else {
resolve(body);
}
});
});
}
跟进编辑。您可以通过捕获错误并再次执行请求来处理“重试”。
requestJSON(url).catch((err) => {
if (err.code === 'ECONNTIMEOUT' || err.code === 'ECONNRESET') {
return requestJSON(url)
} else {
return Promise.reject(err);
}
});
您可能需要添加一些逻辑以在一定数量的重新连接后停止,否则可能会陷入无限循环。
推荐阅读
- java - 使用 LocalDate 将一个日期更改为另一种日期格式
- list - 如何使用 SwiftUI 使这个 ForEach 函数工作?
- android - 我想通过 Web 服务将 android 应用程序与 sql server 连接起来
- javascript - MongoDB不更新数据
- flutter - Flutter 将 SingleChildScrollView 或 ListView 添加到屏幕中的部分小部件
- android-sqlite - 我想在不根据行数据的情况下在完整列中输入数据
- swift - 根据标签文本动态改变单元格高度
- java - 我们如何使用公钥从托管在服务器中的应用程序使用 JSch 连接到 SFTP 服务器
- python - 使用 kubernetes cronjobs 调度 django 命令
- java - 如何生成没有重复号码的 4 位密码