最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - .each and callbacks - Stack Overflow

programmeradmin3浏览0评论

I am using request and cheerio node modules to create get some data out of a website. I'd like to get a list of item and once this list is complete, call an asynchronous function:

request('http://myurl', function(req,res,data){
    var $ = cheerio.load(data);
    var List = [];

    $('.myItems').each(function(i, element){
        console.log( typeof $(this).text() )
        List.push($(this).text());
    });

   for (var i=0; i <  List.length; i++){
      // make an asynchronous call to a API
   }
});

My question is how do I wait for the list to be completed, ie, how can I know than the .each function has looped through all the items?

Can I do this with async?

Thanks

I am using request and cheerio node modules to create get some data out of a website. I'd like to get a list of item and once this list is complete, call an asynchronous function:

request('http://myurl', function(req,res,data){
    var $ = cheerio.load(data);
    var List = [];

    $('.myItems').each(function(i, element){
        console.log( typeof $(this).text() )
        List.push($(this).text());
    });

   for (var i=0; i <  List.length; i++){
      // make an asynchronous call to a API
   }
});

My question is how do I wait for the list to be completed, ie, how can I know than the .each function has looped through all the items?

Can I do this with async?

Thanks

Share Improve this question edited May 22, 2014 at 9:38 axwcode 7,8247 gold badges32 silver badges41 bronze badges asked May 12, 2014 at 11:53 SpearfisherSpearfisher 8,78324 gold badges74 silver badges125 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 14

The .each function of cheerio is synchronous (see source). So as long as you do nothing asynchronous in the callback (which is the case in the question), you have nothing to do : at the following line the loop will be complete.


If you do call asynchronous functions in the loop, the cleanest and simplest solution is to use a Promise library.

In a sequential way (exemple with Bluebird) :

var p = Promise.resolve();
$('.myItems').each(function(i, element){
    p = p.then(function(){ 
         // do something with $(this).text() and return a promise
    });
});
p.then(function(){
   // all asynchronous tasks are finished
});

If sequential requests aren't needed (here an example with Q) :

Q.allSettled($('.myItems').map(function(){
   // return a promise
})).then(function(){
   // all asynchronous tasks are finished
});

Well, for anyone else having this problem, just know that you don't really need to use the each method, you can transform them in an array with the toArray method and iterate over them in a for loop.

That way, there is no need to use an external lib like Bluebird.

request('http://myurl', function(req,res,data){
    var $ = cheerio.load(data);
    var List = [];

    const items = $('.myItems').toArray();
    for(let i = 0; i < items.length; i++){
        const el = items[i];
        console.log( typeof $(el).text() )
        List.push($(el).text());
        // make an asynchronous call to a API
    }
});

You could use the async module to easily handle those kinds of async tasks.

Specifically async.each or async.eachLimit if you need concurrency > 1 or async.eachSeries if you want to go through the items in the array, one at a time.

I have to admit I haven't managed to get Denys Séguret's solution working (for the async calls within .each() loop) - the "after" operation still happens before the .each() loop is completed, however I found a different way, and I hope it helps someone:

var Promises = require('bluebird');

request('http://myurl', function(req,res,data){
    var $ = cheerio.load(data);
    var List = [];

    Promises
    // Use this to iterate serially
    .each($('.myItems').get(), function(el){
        console.log( typeof $(el).text() )
        List.push($(el).text());
    })
    // Use this if order of items is not important
    .map($('.myItems').get(), function(el){
        console.log( typeof $(el).text() )
        List.push($(el).text());
    }, {concurrency:1}) // Control how many items are processed at a time
    // When all of the above are done, following code will be executed
    .then(function(){
        for (var i=0; i <  List.length; i++){
            // make an asynchronous call to a API
        }
    });
});

In this particular code example it looks like you could be doing your async calls within then mapping functions, but you get the gist...

Map: https://github.com/petkaantonov/bluebird/blob/master/API.md#mapfunction-mapper--object-options---promise

Each: https://github.com/petkaantonov/bluebird/blob/master/API.md#eachfunction-iterator---promise

发布评论

评论列表(0)

  1. 暂无评论