簡體   English   中英

Node.js循環異步調用

[英]Node.js looping async calls

我正在為報告系統的端點工作。 節點異步給了我一些問題,盡管我不想強迫它同步。

我們正在使用MongoDB和Mongoose。 我必須查詢集合A的正則表達式,然后對於每個返回的文檔,查詢多個包含的文檔以填充要返回的JSON對象/數組。

除了最終循環查詢(異步啟動並提前返回報告的地方)外,我可以對大多數數據使用populate 有沒有一種優雅的方法可以做到這一點? 還是我應該拆分為一個不同的函數,並多次調用該函數來堅持做這些functions should do only one thing

示例代碼:

A.find({ name: regex }).populate({ path: 'B', populate: { path: 'B.C', model: 'C' } }).exec(function(err, A) {
            var report = [];

            A.map(function(a)){
                report[a.name] = [];

                D.aggregate([
                    {
                        $match: {
                            id: B._id
                        }
                    },
                    {
                        $group: {
                            _id: null,
                            count: { $sum: 1 }
                        }
                    }
                ], function(err, result) {
                    C.map(function(c){
                        report[a.name].push({
                            'field1': c.field1,
                            'field2': c.field2,
                            'field3': c.field3,
                            'count': result.count
                        });
                    });                        
                });
            }

            return report;
        });

這里的問題是邏輯/異步。 不使用語法,因此不使用半偽代碼。

任何幫助或建議,將不勝感激。

您需要使自己熟悉Promise和異步。 因為您要返回一個數組,所以這就是您要獲得的值。

在處理Async時,您有幾種選擇,但是在您的情況下,您想看看兩種解決方案:

// callbacks
getSetOfIDs((err, ids) => {
  let remaining = ids.length;
  let things = [];
  let failed = false;

  ids.forEach(id => {
    getThingByID(id, (err, thing) => {
      if (failed) { return; }
      if (err) {
        failed = true;
        handleFailure(err);
      } else {
        remaining -= 1;
        things.push(thing);
        if (!remaining) {
          handleSuccess(things);
        }
      }
    });
  });
});

注意,我沒有返回任何things ,而是將其傳遞給了回調。

您可以使用高階函數來清理此類事件。

// cleaned up callbacks
function handleNodeCallback (succeed, fail) {
  return function (err, data) {
    if (err) {
      fail(err);
    } else {
      succeed(data);
    }
  };
}

function handleAggregateCallback (succeed, fail, count) {
  let items = [];
  let failed = false;

  const ifNotFailed = cb => data => {
    if (!failed) { cb(data); }
  };

  const handleSuccess = ifNotFailed((item) => {
    items.push(item);
    if (items.length === count) { succeed(items); }
  });

  const handleFailure = ifNotFailed((err) => {
    failed = true;
    fail(err);
  });

  return handleNodeCallback(handleSuccess, handleFailure);
}

稍后提供一些輔助代碼,我們可以開始了:

// refactored callback app code (note that it's much less scary)
getSetOfIDs((err, ids) => {
  const succeed = (things) => app.display(things);
  const fail = err => app.apologize(err);
  if (err) { return fail(err); }

  let onThingResponse = handleAggregateCallback(succeed, fail, ids.length);
  ids.forEach(id => getThingByID(id, onThingResponse));
});

請注意,除了高階函數外,我從不返回任何東西,而是始終傳遞延續(接下來要做的事情,帶有一個值)。

另一種方法是承諾

// Promises
getSetOfIDs()
  .then(ids => Promise.all(ids.map(getThingByID)))
  .then(things => app.display(things))
  .catch(err => app.apologize(err));

要真正了解這里發生的事情,請學習Promises,Promise.all靜態方法和array.map()

從理論上講,這兩組代碼都做同樣的事情,除了在最后一種情況下, getSetOfIDsgetThingByID不接受回調,它們返回的是getThingByID

通常在異步調用中,在return語句之后,任何操作都將被取消。

也許只有在一切都做好之后,您才能返回報告對象。

A.find({ name: regex }).populate({ path: 'B', populate: { path: 'B.C', model: 'C' } }).exec(function(err, A) {
            var report = [];

            A.map(function(a)){
                report[a.name] = D.aggregate([
                    {
                        $match: {
                            id: B._id
                        }
                    },
                    {
                        $group: {
                            _id: null,
                            count: { $sum: 1 }
                        }
                    }
                ], function(err, result) {
                    if(err){
                      return [];
                    }
                    var fields = []
                    C.map(function(c){
                        fields.push({
                            'field1': c.field1,
                            'field2': c.field2,
                            'field3': c.field3,
                            'count': result.count
                        });
                    });
                    return fields;                       
                });     
            }
            return report;
        });

只需使用諾言:

A.find({ name: regex }).populate({ path: 'B', populate: { path: 'B.C', model: 'C' } }).exec(function(err, A) {

   var report = [];
   return Promise.all([
      A.map(function(a)){
         return new Promise(function(resolve, reject) {

            report[a.name] = [];

            D.aggregate([{ $match: { id: B._id }},{$group: {_id: null,count: { $sum: 1 }}}], 
              function(err, result) {
                if(err) {
                  reject(err)
                } else {
                  C.map(function(c){
                    report[a.name].push({
                        'field1': c.field1,
                        'field2': c.field2,
                        'field3': c.field3,
                        'count': result.count
                    });
                  }); 
                  resolve(report)
                }  
            });
        }
    })])
  })
  .then(function(report){
     console.log(report)
   })
  .catch(function(err){
     console.log(err)
   })

暫無
暫無

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

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