Node.js:如何顺序运行asynchronous代码

我有这块代码

User.find({}, function(err, users) { for (var i = 0; i < users.length; i++) { pseudocode Friend.find({ 'user': curUser._id }, function(err, friends) * * ANOTHER CALLBACK * * { for (var i = 0; i < friends.length; i++) { pseudocode } console.log("HERE I'm CHECKING " + curUser); if (curUser.websiteaccount != "None") { request.post({ url: 'blah', formData: blah }, function(err, httpResponse, body) { * * ANOTHER CALLBACK * * pseudocode sendMail(friendResults, curUser); }); } else { pseudocode sendMail(friendResults, curUser); } }); console.log("finished friend"); console.log(friendResults); sleep.sleep(15); console.log("finished waiting"); console.log(friendResults); } }); 

这里有几个asynchronous的事情发生。 对于每个用户,我想find他们的相关朋友,并将它们连接到一个variables。 然后,我想检查用户是否有一个网站帐户,如果是的话,发一个post请求,并在那里获取一些信息。 唯一的问题是,所有的事情都是乱序发生的,因为代码没有等待callback完成。 我一直在睡觉,但是这并没有解决问题,因为它仍然混乱。

我已经看了asynchronous,但这些function交织在一起,并没有真正分开,所以我不知道它是如何与asynchronous工作。

任何build议让这个代码顺序运行?

谢谢!

由于其简单性,我更喜欢诺言模块https://www.npmjs.com/package/promise

 var Promises = require('promise'); var promise = new Promises(function (resolve, reject) { // do some async stuff if (success) { resolve(data); } else { reject(reason); } }); promise.then(function (data) { // function called when first promise returned return new Promises(function (resolve, reject) { // second async stuff if (success) { resolve(data); } else { reject(reason); } }); }, function (reason) { // error handler }).then(function (data) { // second success handler }, function (reason) { // second error handler }).then(function (data) { // third success handler }, function (reason) { // third error handler }); 

正如你所看到的,你可以永远这样继续下去。 你也可以返回简单的值而不是来自asynchronous处理程序的承诺,然后将这些简单的值传递给callback函数。

我重写了你的代码,所以它更容易阅读。 如果要保证同步执行,您可以select做什么:

  1. 使用asynchronous库。 它提供了一些帮助函数,可以串行运行你的代码,特别是: https : //github.com/caolan/async#seriestasks-callback

  2. 使用promise来避免callback,并简化你的代码API。 诺言是JavaScript中的一个新function,但在我看来,你现在可能不想这样做。 承诺的库支持仍然很差,并且不可能将它们用于很多stream行的库:(

现在 – 关于你的程序 – 现在你的代码实际上没有错(假设你在pseucode块中没有asynchronous代码)。 您的代码现在将工作得很好,并将按预期执行。

我build议目前使用async来满足您的顺序需求,因为它既可以在服务器端也可以在客户端使用,基本上可以保证与所有stream行的库一起工作,并且可以很好地使用/testing。

清理下面的代码

 User.find({}, function(err, users) { for (var i = 0; i < users.length; i++) { Friend.find({'user':curUser._id}, function(err, friends) { for (var i = 0; i < friends.length; i++) { // pseudocode } console.log("HERE I'm CHECKING " + curUser); if (curUser.websiteaccount != "None") { request.post({ url: 'blah', formData: 'blah' }, function(err, httpResponse, body) { // pseudocode sendMail(friendResults, curUser); }); } else { // pseudocode sendMail(friendResults, curUser); } }); console.log("finished friend"); console.log(friendResults); sleep.sleep(15); console.log("finished waiting"); console.log(friendResults); } }); 

首先让我们去更多的function

 var users = User.find({}); users.forEach(function (user) { var friends = Friend.find({ user: user._id }); friends.forEach(function (friend) { if (user.websiteaccount !== 'None') { post(friend, user); } sendMail(friend, user); }); }); 

然后让asynchronous

 async.waterfall([ async.apply(Users.find, {}), function (users, cb) { async.each(users, function (user, cb) { async.waterfall([ async.apply(Friends.find, { user, user.id}), function (friends, cb) { if (user.websiteAccount !== 'None') { post(friend, user, function (err, data) { if (err) { cb(err); } else { sendMail(friend, user, cb); } }); } else { sendMail(friend, user, cb); } } ], cb); }); } ], function (err) { if (err) { // all the errors in one spot throw err; } console.log('all done'); }); 

另外,这是你在做一个连接,SQL真的很擅长这些。

你会想看看一些叫promise的东西。 他们会让你连锁事件,并按顺序运行。 这里有一个很好的教程,它们是什么以及如何使用它们http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

您也可以看一下Async JavaScript库: Async它提供了用于命令JavaScript中asynchronous函数执行的实用程序函数。

注意:我认为你在一个处理程序中执行的查询的数量是一种代码味道。 这个问题在查询层面可能会更好解决。 这就是说,让我们继续吧!

很难确切地知道你想要什么,因为你的psuedocode可以使用一个清理恕我直言,但我要去你想要做的是这样的:

  1. 获取所有用户,并为每个用户a。 获取所有用户的朋友和每个朋友:
    • 如果用户有一个网站帐户发送一个发布请求
    • 发送电子邮件
  2. 在这个过程结束之后做一些事情

你可以做很多不同的方法。 香草callback或asynchronous工作伟大; 我会主张承诺,因为他们是未来,图书馆的支持是相当好的。 我将使用rsvp ,因为它很轻,但是任何符合Promise / A +的库都可以做到。

 // helpers to simulate async calls var User = {}, Friend = {}, request = {}; var asyncTask = User.find = Friend.find = request.post = function (cb) { setTimeout(function () { var result = [1, 2, 3]; cb(null, result); }, 10); }; User.find(function (err, usersResults) { // we reduce over the results, creating a "chain" of promises // that we can .then off of var userTask = usersResults.reduce(function (outerChain, outerResult) { return outerChain.then(function (outerValue) { // since we do not care about the return value or order // of the asynchronous calls here, we just nest them // and resolve our promise when they are done return new RSVP.Promise(function (resolveFriend, reject){ Friend.find(function (err, friendResults) { friendResults.forEach(function (result) { request.post(function(err, finalResult) { resolveFriend(outerValue + '\n finished user' + outerResult); }, true); }); }); }); }); }, RSVP.Promise.resolve('')); // handle success userTask.then(function (res) { document.body.textContent = res; }); // handle errors userTask.catch(function (err) { console.log(error); }); }); 

jsbin