让forEach等待回调

时间:2016-12-16 07:36:08

标签: javascript node.js

异步编程新手,所以我无法知道如何执行此操作:

$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前进到下一个元素,直到完成。

3 个答案:

答案 0 :(得分:3)

使用async包。

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

注意:回调的行为类似于return。一旦回调符合价值,它就不会继续进行。现在它将发送下一个产品的请求。

答案 1 :(得分:2)

由于forEach是同步的并且请求是异步的,因此无法完全按照您的描述进行操作。然而,你可以做的是创建一个处理docs数组中的一个项目并删除它的函数,然后当你完成处理时,转到下一个:

chcp 1252 

::::::::::::::::::::::::::::::::::::::::::::
:: Run as admin
::::::::::::::::::::::::::::::::::::::::::::
@echo off
CLS
ECHO.
ECHO =============================
ECHO The name of the main folder?    
ECHO =============================

:init
setlocal DisableDelayedExpansion
set "batchPath=%~0"
for %%k in (%0) do set batchName=%%~nk
set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
setlocal EnableDelayedExpansion

:checkPrivileges
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )

:getPrivileges
if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
ECHO.
ECHO **************************************
ECHO Invoking UAC for Privilege Escalation
ECHO **************************************

ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
ECHO args = "ELEV " >> "%vbsGetPrivileges%"
ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
ECHO Next >> "%vbsGetPrivileges%"
ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >>      "%vbsGetPrivileges%"
"%SystemRoot%\System32\WScript.exe" "%vbsGetPrivileges%" %*
exit /B

:gotPrivileges
setlocal & pushd .
cd /d %~dp0
if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)

::::::::::::::::::::::::::::
::START
::::::::::::::::::::::::::::

::Dialogwindow.
@echo off
title message

echo Skriv nedan^^!
set/p "Projektnamn=>"

:: Creates the folder structure
md "%~dp0"%Projektnamn%

md "%~dp0""%Projektnamn%"/"1. Projektpärm"
md "%~dp0""%Projektnamn%"/"1. Projektpärm"/"1. Tekniska specifikationer"
md "%~dp0""%Projektnamn%"/"1. Projektpärm"/"2. ÄTA,Beställning & Fakturering"
md "%~dp0""%Projektnamn%"/"1. Projektpärm"/"3. Tidplan"
md "%~dp0""%Projektnamn%"/"1. Projektpärm"/"4. Projektmöten"
md "%~dp0""%Projektnamn%"/"1. Projektpärm"/"5. Dagbok"
md "%~dp0""%Projektnamn%"/"1. Projektpärm"/"6. Kvalitet"
:: Genväg enlinjeschema, se kod för genväg

md "%~dp0""%Projektnamn%"/"2. Kretsschema"
md "%~dp0""%Projektnamn%"/"2. Kretsschema"/"1. Konstruktionsunderlag från    kund"

md "%~dp0""%Projektnamn%"/"3. Stationsdokumentation"
md "%~dp0""%Projektnamn%"/"3. Stationsdokumentation"/"1. Enlinjeschema"
md "%~dp0""%Projektnamn%"/"3. Stationsdokumentation"/"2. Blockschema"
md "%~dp0""%Projektnamn%"/"3. Stationsdokumentation"/"3. Förreglingsschema"
md "%~dp0""%Projektnamn%"/"3. Stationsdokumentation"/"4. Ritningsnummer"

md "%~dp0""%Projektnamn%"/"4. Skåpsdokumentation"
md "%~dp0""%Projektnamn%"/"4. Skåpsdokumentation"/"1. Skåpslayout"
md "%~dp0""%Projektnamn%"/"4. Skåpsdokumentation"/"2. Apparatlista"
md "%~dp0""%Projektnamn%"/"4. Skåpsdokumentation"/"3. Förbindningstabell"
md "%~dp0""%Projektnamn%"/"4. Skåpsdokumentation"/"4. Skyltlista & Etiketter"

:: Skapar RELATIVA Genvägarna
mklink /D "%~dp0%Projektnamn%\1. Projektpärm\7. Enlinjeschema" "..\3.     Stationsdokumentation\1. Enlinjeschema"
mklink /D "%~dp0%Projektnamn%\1. Projektpärm\8. Blockschema" "..\3.    Stationsdokumentation\2. Blockschema"
mklink /D "%~dp0%Projektnamn%\1. Projektpärm\9. Förreglingsschema" "..\3. Stationsdokumentation\3. Förreglingsschema"
mklink /D "%~dp0%Projektnamn%\1. Projektpärm\10. Skåpslayout" "..\4. Skåpsdokumentation\1. Skåpslayout"

pause

修改

将产品从数据库移至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 ); } ); } 变量,并将productsToProcess更改为不再需要参数。

答案 2 :(得分:0)

您无法在Array.prototype.forEach()内等待异步操作。

考虑到您用于Google的请求所使用的库与Promises不兼容,使用Async可能是您案例中的快速解决方案(对于大型项目或Promise兼容库,我建议使用Promise方式)

Async map允许你在里面使用异步操作,因为它等待回调。

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

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

如果您对上述代码有任何疑问,请与我们联系。