为什么async.series只生成一个输出?

时间:2016-02-29 21:03:06

标签: javascript node.js asynchronous

我有以下代码,使用randomuser APIgotasync.series发出10个请求。由于某些原因,这只给我一个输出。我该如何解决这个问题?

const got = require('got');
const async = require('async');

var tenOperations = [];
for(var i = 0; i < 10; i++) {
  tenOperations.push(doRequest);
}
function doRequest(callback) {
  got('https://randomuser.me/api/')
  .then(response => {
    console.log(response.body);
  })
  .catch(error => {
    console.log(error.response.body);
  });
};

async.series(tenOperations, function(err, results) {
  if(err) console.log(err);
  console.log(results);
});

Here is the sample output

     {
    "results": [
        {
            "user": {
                "gender": "female",
                "name": {
                    "title": "miss",
                    "first": "غزل",
                    "last": "كامياران"
                },
                "location": {
                    "street": "6186 آزادی",
                    "city": "رشت",
                    "state": "تهران",
                    "zip": 64318
                },
                "email": "غزل.كامياران@example.com",
                "username": "goldenpanda201",
                "password": "muscles",
                "salt": "OStU2tyA",
                "md5": "92ac8a84380a24785597d0e916b0174e",
                "sha1": "93f6e830538dbc557017011583cca3b5e527f854",
                "sha256": "99a4c35237b1ebe276732fbf62efca24fd457428853de8a967dd465b80b82f0f",
                "registered": 1352433856,
                "dob": 1370066399,
                "phone": "053-14062122",
                "cell": "0929-641-1309",
                "picture": {
                    "large": "https://randomuser.me/api/portraits/women/48.jpg",
                    "medium": "https://randomuser.me/api/portraits/med/women/48.jpg",
                    "thumbnail": "https://randomuser.me/api/portraits/thumb/women/48.jpg"
                }
            }
        }
    ]
}

2 个答案:

答案 0 :(得分:1)

如果您只是想按顺序对同一API发出10个请求(一个接一个,而不是并行),您可以这样做:

const got = require('got');

function runSequence(url, num) {
    let cntr = 0;
    let results = [];
    return new Promise(function(resolve, reject) {

        function checkDone(data) {
            ++cntr;
            results.push(data);
            if (cntr < num) {
                next();
            } else {
                resolve(results);
            }
        }

        function next() {
            got(url).then(response => {
                console.log(response.body);
                checkDone(response.body);
            }).catch(error => {
                console.log(error.response.body);
                checkDone(null);
            });
        }
        next();
    });
}

runSequence('https://randomuser.me/api/', 10).then(function(results) {
    // access array of results here
});

如果不必一次一个地完成API调用,并且您可以同时将它们全部投放,那么您可以这样做:

function runParallel(url, num) {
    let promises = [];
    for (let i = 0; i < num; i++) {
        promises.push(got(url));
    }
    return Promise.all(promises);
}

runParallel('https://randomuser.me/api/', 10).then(function(results) {
    // access array of results here
});

注意:并行选项在第一个错误时中止,而此处显示的两个序列选项都会继续出错。要么可以改为其他行为。你没有说明你想要的东西。

这是一种稍微不同的运行序列的方式:

function runSequence(url, num) {
    let cntr = 0;
    let results = [];

    function checkDone(data) {
        ++cntr;
        results.push(data);
        if (cntr < num) {
            return next();
        } else {
            return results;
        }            
    }

    function next() {
        return got(url).then(response => {
            return checkDone(response.body);
        }).catch(error => {
            return checkDone(null);
        });
    }

    return next();
}

这是一个用于重复一次异步操作N次的通用函数。您传入异步函数(返回一个promise),您希望它按顺序重复的次数,以及您是希望它在错误时继续还是在出错时中止。

// pass a function that returns a promise
function repeatSequence(fn, num, continueOnError) {
    let cntr = 0;
    let results = [];

    checkDone(data) {
        ++cntr;
        results.push(data);
        if (cntr < num) {
            return next();
        } else {
            return results;
        }
    }

    function next() {
        return fn().then(checkDone).catch(function(err) {
            if (continueOnError) {
                return checkDone(null);
            } else {
                // reject on error
                throw err;
            }
        });
    }

    return next();
}

并且,如果您使用Bluebird Promise library,则可以使用Promise.mapSeries()

function repeatSequence(fn, num, continueOnError) {
    var array = new Array(num);
    return Promise.mapSeries(array, function () {
        return fn().catch(function (err) {
            if (continueOnError) {
                return null;
            } else {
                throw (err);
            }
        });
    });
}

或者,如果您不想要continueOnError选项,它就会变为:

function repeatSequence(fn, num) {
    var array = new Array(num);
    return Promise.mapSeries(array, fn);
}

答案 1 :(得分:1)

我必须同意Bergi的观点,即我们不能将回调与承诺混在一起。尽管它们都是异步机制,但这两种结构中的每一种在本质上和哲学上都是不相同的。

然而,正如jfriend00指出的那样,主要的问题是&#34;回调&#34;在每一步都没有被召唤。在使用非承诺库时,这将是正确的解决方案。

<强>解决方案

请在下面找到我的修改。本质是:

  1. 此解决方案是一个如何使用promises实现它的示例。
  2. async-q之类的端口可能会帮助您更好地利用现有库。

    const got = require('got');
    const async_q = require('async-q');
    
    var tenOperations = [];
    for(var i = 0; i < 10; i++) {
        tenOperations.push(doRequest);
    }
    
    function doRequest() {
        return got('https://randomuser.me/api/')
        .then(response => {
            console.log('resp', response.body);
            return JSON.parse(response.body);
        })
        .catch(error => {
            console.log('error', error.response.body);
            return error.response.body;
        });
    }
    
    async_q
    .series(tenOperations)
    .then (results => {
        console.log('results', results);
    })
    .done();