簡體   English   中英

Node.js:如何順序運行異步代碼

[英]Node.js: How to run asynchronous code sequentially

我有這大塊代碼

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

這里有幾個異步的事情發生。 對於每個用戶,我想找到他們相關的朋友並將他們連接到一個變量。 然后,我想檢查該用戶是否有網站帳戶,如果有,請發帖請求並在那里獲取一些信息。 唯一的問題是,由於代碼沒有等待回調完成,所有事情都發生了故障。 我一直在使用睡眠,但這並沒有解決問題,因為它仍然是混亂的。

我已經研究過異步,但是這些函數是交織在一起而不是真正分開的,所以我不確定它是如何與異步一起工作的。

有什么建議讓這段代碼順序運行?

謝謝!

由於其簡單性,我更喜歡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. 使用異步庫。 它提供了一些幫助函數來運行你的代碼,特別是,這個: https//github.com/caolan/async#seriestasks-callback

  2. 使用promises可以避免進行回調,並簡化代碼API。 Promise是Javascript中的一項新功能,但在我看來,您可能不想立即執行此操作。 對於承諾仍然存在較差的庫支持,並且不可能將它們與許多流行的庫一起使用:(

現在 - 關於你的程序 - 你的代碼實際上沒有任何問題(假設你在pseucode塊中沒有異步代碼)。 您的代碼現在可以正常工作,並將按預期執行。

我建議目前使用async來滿足您的順序需求,因為它既適用於服務器端又適用於客戶端,基本上可以保證與所有流行的庫一起使用,並且使用/測試得很好。

清理下面的代碼

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

首先讓我們更具功能性

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非常擅長這些。

你會想要研究一些叫做承諾的東西。 它們允許您鏈接事件並按順序運行它們。 這是一個很好的教程,介紹它們是什么以及如何使用它們http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

您還可以查看Async JavaScript庫: Async它提供了用於在JavaScript中命令執行異步函數的實用程序函數。

注意:我認為您在處理程序中執行的查詢數量是代碼味道。 在查詢級別可能更好地解決此問題。 那就說了,我們繼續吧!

很難確切地知道你想要什么,因為你的偽代碼可以使用清理恕我直言,但我要去你想做的是這樣的:

  1. 獲取所有用戶以及每個用戶a。 得到所有用戶的朋友和每個朋友:
    • 如果用戶擁有網站帳戶,則發送帖子請求
    • 發送電子郵件
  2. 在完成該過程后執行某些操作

你可以用很多不同的方式做到這一點 香草回調或異步工作很棒; 我將提倡承諾,因為它們是未來,圖書館的支持非常好。 我將使用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM