简体   繁体   中英

Promisify callbacks that use rate limiter

I'm working on a project where I have to traverse a couple of api's and download multple pages on each. I have the code working using callbacks but I'm not sure how to I can run code after the last http request completes.

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!')
                    });     
                }           
            });     
        }
    }
});

I think the correct thing would be to nest all the functions within promises and use Promise.all on each one. The problem is with the rate limiter I think the nested promises won't be added to the promises array before Promise.all is called.

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!')});
});

If you plan to use Promises with the limiter module then you may want to create a simple wrapper method for removing a token. It would also be ideal to use the request-promise module, or create another simple wrapper for making requests as well.

That would look something like this:

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);
});

And if you choose to create a simple wrapper instead of request-promise :

function requestJSON(url) {
    return Promise((resolve, reject) => {
        request(url, {json: true}, (err, res, body) => {
            if (err) {
                reject(err);
            } else {
                resolve(body);
            }
        });
    });
}

Follow up edit. You can handle "retry" by catching the error and performing the request again.

requestJSON(url).catch((err) => {
    if (err.code === 'ECONNTIMEOUT' || err.code === 'ECONNRESET') {
        return requestJSON(url)
    } else {
        return Promise.reject(err);
    }
});

You'll probably need to add some logic for stopping after some amount of reties or this may end up in an infinite loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM