Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/409.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Node.js:执行多个异步操作的最佳方法,然后再执行其他操作?_Javascript_Node.js_Promise - Fatal编程技术网

Javascript Node.js:执行多个异步操作的最佳方法,然后再执行其他操作?

Javascript Node.js:执行多个异步操作的最佳方法,然后再执行其他操作?,javascript,node.js,promise,Javascript,Node.js,Promise,在下面的代码中,我试图一次完成多个(大约10个)HTTP请求和RSS解析 我在需要访问和解析结果的URI数组上使用标准的forEach构造 代码: 我知道当调用函数一次时,我应该使用回调。然而,在本例中,我能想到的使用回调的唯一方法是调用一个函数,该函数计算它被调用的次数,并且只有当它被调用的次数与feedsToFetch.length相同时才会继续,这似乎有点骇人听闻 所以我的问题是,在node.js中处理这种情况的最佳方法是什么 最好没有任何形式的阻塞!(我仍然想要那炽热的速度)。是承诺还是

在下面的代码中,我试图一次完成多个(大约10个)HTTP请求和RSS解析

我在需要访问和解析结果的URI数组上使用标准的
forEach
构造

代码:

我知道当调用函数一次时,我应该使用回调。然而,在本例中,我能想到的使用回调的唯一方法是调用一个函数,该函数计算它被调用的次数,并且只有当它被调用的次数与
feedsToFetch.length
相同时才会继续,这似乎有点骇人听闻

所以我的问题是,在node.js中处理这种情况的最佳方法是什么

最好没有任何形式的阻塞!(我仍然想要那炽热的速度)。是承诺还是别的什么

谢谢,
Danny

使用url列表的副本作为队列来跟踪到达的情况,这样做很简单: (所有更改均已评论)


最后,这并没有承诺的那么“肮脏”,并且为您提供了重新加载未完成URL的机会(仅计数器无法告诉您哪个失败)。对于这个简单的并行下载任务,它实际上将向您的项目中添加更多的代码实现承诺,而不是简单的队列,而且Promise.all()不是最直观的地方。一旦您进入子查询,或者想要比火车失事更好的错误处理,我强烈建议您使用承诺,但您不需要使用火箭发射器来杀死松鼠…

无需任何黑客操作

我建议使用该模块,因为它使这类事情变得更容易

async
作为
arr.forEach
的异步替代品提供,并允许您在完成后传递
done
回调函数。它将处理一系列的每个项目,就像
forEach
一样。此外,它还可以方便地将错误冒泡到回调中,这样就不必在循环中包含处理程序逻辑。如果需要并行处理,可以使用

async.eachSeries
调用和回调之间将不存在任何阻塞

async.eachSeries(feedsToFetch, function(feedUri, done) {

  // call your async function
  feed(feedUri, function(err, feedArticles) {

    // if there's an error, "bubble" it to the callback
    if (err) return done(err);

    // your operation here;
    articles = articles.concat(feedArticles);

    // this task is done
    done();
  });
}, function(err) {

  // errors generated in the loop above will be accessible here
  if (err) throw err;

  // we're all done!
  console.log("all done!");
});
或者,您可以构建一个异步操作数组并将其传递给。Series将以系列处理结果(非并行),并在每个函数完成时调用回调。如果您更喜欢熟悉的
arr.forEach
语法,则可以在
async.eachSeries
上使用此选项

// create an array of async tasks
var tasks = [];

feedsToFetch.forEach(function (feedUri) {

  // add each task to the task array
  tasks.push(function() {

    // your operations
    feed(feedUri, function(err, feedArticles) {
      if (err) throw err;
      articles = articles.concat(feedArticles);
    });
  });
});

// call async.series with the task array and callback
async.series(tasks, function() {
 console.log("done !");
});

或者你可以自己滚™

也许你觉得自己雄心勃勃,或者你不想依赖于
async
依赖关系。也许你只是和我一样无聊。无论如何,我特意复制了
async.eachSeries
的API,以便于理解其工作原理

一旦我们删除了这里的注释,我们只有9行代码可以被异步处理的任何数组重用!它不会修改原始数组,可以发送错误以“短路”迭代,并且可以使用单独的回调。它还可以处理空数组。只需9行即可实现相当多的功能:)

现在,让我们创建一个示例异步函数来使用它。我们将在这里用500毫秒的设置超时来模拟延迟

// void sampleAsync(String uri, Function done)
//   * done receives message string after 500 ms
function sampleAsync(uri, done) {

  // fake delay of 500 ms
  setTimeout(function() {

    // our operation
    // <= "foo"
    // => "async foo !"
    var message = ["async", uri, "!"].join(" ");

    // call done with our result
    done(message);
  }, 500);
}
输出(每次输出前延迟500毫秒)


无黑客解决方案

流行的Promise库为此用例提供了一个
.all()
方法(等待一系列异步调用完成,然后执行其他操作)。它与你的场景完美匹配

Bluebird还有
.map()
,它可以获取一个值数组并使用它来启动承诺链

下面是一个使用蓝鸟
.map()
的示例:

还要注意,您不需要在这里使用闭包来累积结果

这些代码也写得很好,有很多例子,因此更容易理解

一旦我学会了承诺模式,生活就变得轻松多了。我怎么推荐都不够

此外,这里还介绍了使用Promission、
async
模块和其他模块处理异步函数的不同方法


希望这有帮助

是的,承诺是最简单的方式。如果您想使用标准JS编写自己的解决方案,可以很容易地进行调整。请显示
feed()
函数的代码。@Amadan,也许这是个人偏好,但我认为承诺不是“最简单”的方法。我已经提供了一个答案来进一步说明这一点。您已经找到了答案:使用计数器并计算提出和完成的请求数。这基本上就是async和Promissions在内部所做的。大约在异步库编写前一年,我写了这个答案来解决这个确切的问题:。这是一个更高级的实现,允许您启动批量异步操作:@naomik:我想说的是个人偏好-我看不出aarosil的答案比您的答案复杂得多。注意,是node/v8 vNext,它会有收益。这里的asyncEach的行为更像
async.eachSeries
而不是
async.each
fwiw<代码>异步。每个都将以最快的速度(即并发)发出http请求(或其他任何请求)
asynSeries
(或此处的“串行”解决方案)将一个接一个地执行。“除非我弄错了。”红杉道,接得好。我已将自定义的
asyncEach
重命名为
asyncForEach
,以表示它执行类似
arr.forEach
的串行处理。我还将
async.each
更新为
async.eachSeries
。谢谢“不需要黑客……使用这个库”-我不遵循这个逻辑,另一方面,NodeJS附带的承诺会让这个变得微不足道。@BenjaminGruenbaum所以当你说node时
// create an array of async tasks
var tasks = [];

feedsToFetch.forEach(function (feedUri) {

  // add each task to the task array
  tasks.push(function() {

    // your operations
    feed(feedUri, function(err, feedArticles) {
      if (err) throw err;
      articles = articles.concat(feedArticles);
    });
  });
});

// call async.series with the task array and callback
async.series(tasks, function() {
 console.log("done !");
});
// void asyncForEach(Array arr, Function iterator, Function callback)
//   * iterator(item, done) - done can be called with an err to shortcut to callback
//   * callback(done)       - done recieves error if an iterator sent one
function asyncForEach(arr, iterator, callback) {

  // create a cloned queue of arr
  var queue = arr.slice(0);

  // create a recursive iterator
  function next(err) {

    // if there's an error, bubble to callback
    if (err) return callback(err);

    // if the queue is empty, call the callback with no error
    if (queue.length === 0) return callback(null);

    // call the callback with our task
    // we pass `next` here so the task can let us know when to move on to the next task
    iterator(queue.shift(), next);
  }

  // start the loop;
  next();
}
// void sampleAsync(String uri, Function done)
//   * done receives message string after 500 ms
function sampleAsync(uri, done) {

  // fake delay of 500 ms
  setTimeout(function() {

    // our operation
    // <= "foo"
    // => "async foo !"
    var message = ["async", uri, "!"].join(" ");

    // call done with our result
    done(message);
  }, 500);
}
tasks = ["cat", "hat", "wat"];

asyncForEach(tasks, function(uri, done) {
  sampleAsync(uri, function(message) {
    console.log(message);
    done();
  });
}, function() {
  console.log("done");
});
async cat !
async hat !
async wat !
done
var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));

function processAllFeeds(feedsToFetch) {    
    return Promise.map(feedsToFetch, function(feed){ 
        // I renamed your 'feed' fn to 'processFeed'
        return processFeed(feed) 
    })
    .then(function(articles){
        // 'articles' is now an array w/ results of all 'processFeed' calls
        // do something with all the results...
    })
    .catch(function(e){
        // feed server was down, etc
    })
}

function processFeed(feed) { 
    // use the promisified version of 'get'
    return request.getAsync(feed.url)... 
}