为每个等待callback

新的asynchronous编程,所以我只是不知道如何做到这一点:

$results = []; products.forEach(function (product) { // 1. Search ... google(keyword, function (err, res) { if (err) console.error(err) for (var i = 0; i < res.links.length; ++i) { var result = res.links[i]; var obj = { title: res.links[i].title, href: res.links[i].href, description: res.links[i].description } results.push(obj); // 2. store each result in results Array } }, processData); // 3. send all results to processData when done // 5. NOW, itereate further ... }); function processData(results) { console.log('processing data'); // 4. save results to DB } 

由于这个过程需要做HTTP请求,收集数据,然后保存到DB,这都需要时间,所以我不希望forEach推进到下一个元素,直到一个完成。

由于forEach是同步的,并且请求是asynchronous的,因此无法完全按照您的描述进行操作。 但是,您可以执行的操作是创build一个处理docs数组中的一个项目并将其删除的函数,然后在处理完成后转到下一个:

 var results; var productsToProcess; MongoClient.connect( 'mongodb://localhost:27017/suppliers', function ( err, db ) { assert.equal( null, err ); var findDocuments = function ( db ) { var collection = db.collection( 'products' ); collection.find( { $and: [ { "qty": { $gt: 0 } }, { "costex": { $lte: 1000.0 } } ] }, { "mpn": 1, "vendor": 1, "_id": 0 } ).limit( 1 ).toArray( function ( err, products ) { assert.equal( err, null ); productsToProcess = products; getSearching(); db.close(); } ); } findDocuments( db ); } ); function getSearching() { if ( productsToProcess.length === 0 ) return; var product = productsToProcess.splice( 0, 1 )[0]; var keyword = product[ 'vendor' ] + ' "' + product[ 'mpn' ] + '"'; google( keyword, function ( err, res ) { if ( err ) console.error( err ) for ( var i = 0; i < res.links.length; ++i ) { var result = res.links[ i ]; var obj = { title: res.links[ i ].title, href: res.links[ i ].href, description: res.links[ i ].description } results.push( obj ); } }, processData ); } function processData( results ) { MongoClient.connect( 'mongodb://localhost:27017/google', function ( err, db ) { assert.equal( null, err ); // insert document to DB var insertDocuments = function ( db, callback ) { // Get the documents collection var collection = db.collection( 'results' ); // Insert some documents collection.insert( results, function ( err, result ) { assert.equal( err, null ); console.log( "Document inserted" ); callback( result ); db.close(); } ); } insertDocuments( db, getSearching ); } ); } 

编辑

将产品从数据库移至productsToProcessvariables,并将getSearching()更改为不再需要参数。

使用asynchronous包。

 async.eachSeries(docs, function iteratee(product, callback) { // 1. Search ... google(keyword, function (err, res) { if (err) { console.error(err) callback(results) // this will send a fail callback. } for (var i = 0; i < res.links.length; ++i) { var result = res.links[i]; var obj = { title: res.links[i].title, href: res.links[i].href, description: res.links[i].description } results.push(obj); // 2. store each result in results Array callback(null, results) // this is a success callback } }, processData); // 3. send all results to processData when done }); 

注意:callback行为像返回。 一旦callback符合价值,它将不会继续进行。 现在它会发送下一个产品的请求。

您不能等待Array.prototype.forEach()asynchronous操作。

考虑到你正在使用的谷歌请求库与Promises不兼容也许使用asynchronous可能是一个快速的解决scheme(对于大项目或Promise兼容库,我build议Promise的方式)。

asynchronous映射允许你在里面使用asynchronous操作,因为它等待callback。

在你的情况下,这将是这样的事情我猜:

 async.map(products, function(product, callback) { var keyword = product['vendor'] + ' "' + product['mpn'] + '"'; google( keyword, function (err,res) { if (err) { // if it fails it finish here return callback(err); } // using map here makes it easier to loop through the results var results = res.links.map(function(link) { return { title: link.title, href: link.href, description: link.description }; }); callback(null, results); }); }, processData); 

如果您对上述代码有任何疑问,请告诉我。