Nodejs:async和forEach问题 - 需要等待异步解析

时间:2018-06-01 16:42:36

标签: javascript node.js

我对节点很新,而且我一直在学习和处理异步/承诺。现在我尝试创建一个从DB(例如10K行)插入的进程,调用一个Web服务来转换一个列,然后插入修改后的数据。

所以,我做了一个Oracle SQL查询,为了结果,我做了一个foreach:

let counter = 0;
var binds = [];

res.rows.forEach((row) => {

    var original_data = row[0];

    Transform(original_data).then(new_data => {

        counter++;

        binds.push([original_data,new_data]);

        if (counter % 1000 === 0){
            console.log(`1K rows`);
            doInserts(binds);
            binds = [];
        }

    });

});

我每1000行调用一次doInserts,所以我不会在Oracle上打开很多交易。

Transform函数调用webservice,它使用我需要的值解析。

function Transform(value){

    return new Promise(function(resolve, reject){
        var requestPath = `http://localhost:3000/transform/${value}`;
        var req = request.get(requestPath, function(err, response, body){
            if (!err && response.statusCode == 200){
                resolve(body);
            }else{
                reject("API didn't respond.");
            }
        }).end();
    });

}

然而,当foreach有10K行时,这会阻塞web服务(我使用request库来连接)。我在想,foreach并没有同时进行一次变换。

这可能是我不知道很多节点,异步,承诺..但我很困惑。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:3)

你正在同时做很多要求。尝试设置一个并发。您可以使用蓝鸟的Promise.map:http://bluebirdjs.com/docs/api/promise.map.html

await Promise.map(rows, async (row) => {
    const new_data = await Transform(row[0])
    ...
}, {concurrency: 3})  // allow max 3 request at the same time

答案 1 :(得分:0)

您可以使用任何承诺库或ES6 Promise来收集一系列承诺并一起解决它们。

在此示例中,我将使用bluebird

Promise.all

请注意{{1}}将尝试并行解析数组中的所有promise。如果您的数据库有连接数限制,则某些调用可能会失败。

答案 2 :(得分:0)

如果一个失败并且所有成功都失败了(如果最后一个拒绝你丢失了除最后一个结果之外的所有成功),则所选答案会拒绝。

这是适合您情况的代码,可以找到有关代码的更多信息here它不使用bluebird但使用lib.throttle(来自lib,其中包含我写的一般有用的函数)

//lib comes from: https://github.com/amsterdamharu/lib/blob/master/src/index.js
const lib = require("lib");

const Fail = function(reason){this.reason=reason;};
const isFail = o=>(o&&o.constructor)===Fail;
const isNotFail = o=>!isFail(o);
const handleBatch = results =>{//this will handle results of a batch
  //failed are the requests that failed
  //you may want to save the ones that failed to file or something
  const failed = results.filter(isFail);
  const successes = results.filter(result=>!isFail(result));
  return doInserts(successes);
};
const processor = throttler => row =>
  throttler(//throttling Transform to max 10 active
    row=>
      Transform(row[0])
      .then(new_data =>[row[0],new_data])
    )(row)
    .catch(err=>new Fail([err,row]))//catch reject and resolve with fail object
;
//start the process
lib.batchProcess (handleBatch) (1000) (processor(lib.throttle(10))) ([]) (res.rows)
.then(
  results=>console.log("Process done")
  ,err=>console.error("This should not happen:".err)
);