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

时间:2014-12-31 17:27:57

标签: javascript 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);
    }
});

这里发生了一些异步事情。对于每个用户,我想找到他们相关的朋友并将他们连接到一个变量。然后,我想检查该用户是否有网站帐户,如果有,请发帖请求并在那里获取一些信息。唯一的问题是,由于代码没有等待回调完成,所有事情都发生了故障。我一直在使用睡眠,但这并没有解决问题,因为它仍然混乱。

我已经研究过异步,但是这些函数是交织在一起而不是真正分开的,所以我不确定它是如何与异步一起工作的。

是否有任何建议让这段代码按顺序运行?

谢谢!

6 个答案:

答案 0 :(得分:5)

由于其简单性,我更喜欢q https://www.npmjs.com/package/promise的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
});

如您所见,您可以永远这样继续下去。您也可以从异步处理程序返回简单值而不是promises,然后将这些值简单地传递给then回调。

答案 1 :(得分:2)

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

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

  2. 使用promises避免进行回调,并简化代码API。 Promise是Javascript中的一项新功能,但在我看来,您可能不想立即执行此操作。对承诺的图书馆支持仍然很差,并且不可能将它们与许多流行的图书馆一起使用:(

  3. 现在 - 关于你的程序 - 现在你的代码实际上没有任何问题(假设你没有在pseucode块中有异步代码)。您的代码现在可以正常工作,并将按预期执行。

    我建议目前使用异步来满足您的顺序需求,因为它既适用于服务器端又适用于客户端,基本上可以保证与所有流行的库一起使用,并且使用/测试都很好。

    清理下面的代码

    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);
      }
    });
    

答案 2 :(得分:2)

首先让我们更具功能性

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);
  });
});

然后让异步

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非常擅长这些。

答案 3 :(得分:1)

你会想要看一些叫做承诺的东西。它们允许您链接事件并按顺序运行它们。这是一个很好的教程,介绍它们是什么以及如何使用它们http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

答案 4 :(得分:1)

您还可以查看Async JavaScript库:Async它提供了用于在JavaScript中命令执行异步函数的实用程序函数。

答案 5 :(得分:1)

注意:我认为您在处理程序中执行的查询数量是代码味道。在查询级别可能更好地解决此问题。那就是说,让我们继续!

很难确切地知道你想要什么,因为你的伪代码可以使用清理恕我直言,但我想要做的就是:

  1. 获取所有用户以及每位用户 一个。得到所有用户的朋友和每个朋友:
    • 如果用户拥有网站帐户,则发送帖子请求
    • 发送电子邮件
  2. 在流程完成后执行某些操作
  3. 你可以通过许多不同的方式做到这一点。香草回调或异步工作很棒;我将提倡promises,因为他们是未来,图书馆的支持非常好。我会使用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