节点JS Api请求循环

时间:2017-10-17 20:28:17

标签: node.js loops callback request

我试着用我的Node JS避免回调地狱。但是我试图发出大量的api请求并将这些请求插入到我的数据库中。

我的问题(当然)是我的for-loop运行并在我完成请求和数据库插入之前递增i。

for(var i = 0; i <= 1 ; i++){
    apiRequest = data[i]; 
    apicall(apiRequest);
}


function apicall(urlApi){
    request((urlApi), function(error, response, body){
        if(error){
            console.log("error");
        } else if(!error && response.statusCode == 200){
            var myobj = JSON.parse(body);
            dbInsert(myobj);
        }
    });
}

function dbInsert(obj) {
    //insert into database
}

如果其他人会接受这个问题,我可以真正推荐这篇博文,我在阅读 joshvermaire 的回复后找到了这篇博文:

http://www.sebastianseilund.com/nodejs-async-in-practice

2 个答案:

答案 0 :(得分:3)

有很多方法可以解决这类问题。首先,如果你可以并行运行所有API调用(同时在飞行中运行)并且它们在数据库中插入的顺序无关紧要,那么通过这样做可以更快地获得结果(vs按顺序序列化它们。

在下面的所有选项中,您将使用以下代码:

const rp = require('request-promise');

function apicall(urlApi){
    return rp({url: urlApi, json: true}).then(function(obj){
        return dbInsert(obj);
    });
}

function dbInsert(obj) {
    //insert into database
    // return a promise that resolves when the database insertion is done
}

并行使用ES6标准承诺

let promises = [];
for (let i = 0; i <= data.length; i++) {
    promises.push(apicall(data[i]));
}

Promise.all(promises).then(() => {
    // all done here
}).catch(err => {
    // error here
});

并行使用Bluebird Promise Library

使用Bluebird Promise库,您可以使用Promise.map()来迭代您的阵列,并且可以将concurrency选项传递给它以控制可能在同一时间保持的异步调用数量压倒数据库或目标API主机,可能有助于控制最大内存使用量。

Promise.map(data, apiCall, {concurrency: 10}).then(() => {
    // all done here
}).catch(err => {
    // error here
});

使用标准ES6承诺系列

如果由于某些原因(例如按顺序插入数据库)而必须序列化它们,那么你可以这样做。下面显示的.reduce()模式是使用标准ES6序列化阵列上的承诺操作的经典方法:

data.reduce(data, (p, item) => {
    return p.then(() => {
        return apicall(item);
    });
}, Promise.resolve()).then(() => {
    // all done here
}).catch(err => {
    // error here
});

在系列中使用蓝鸟承诺

Bluebird有一个Promise.mapSeries(),它会串行迭代一个数组,调用一个函数,该函数返回数组中每个项目的一个promise,这比手动操作要简单一些。

Promise.mapSeries(data, apiCall).then(() => {
    // all done here
}).catch(err => {
    // error here
});

答案 1 :(得分:1)

我建议使用async.each之类的内容。然后你可以这样做:

async.each(data, function(apiRequest, cb) {
    apicall(apiRequest, cb);
}, function(err) {
   // do something after all api requests have been made
});

function apicall(urlApi, cb){
    request((urlApi), function(error, response, body){
        if(error){
            console.log("error");
            cb(error);
        } else if(!error && response.statusCode == 200){
            var myobj = JSON.parse(body);
            dbInsert(myobj, cb);
        }
    });
}

function dbInsert(obj, cb) {
    doDBInsert(obj, cb);
}

dbInsert方法完成时,请确保调用cb回调。如果您需要在一系列中执行此操作,请查看async.eachSeries