javascript - Node.js 循环异步调用

标签 javascript node.js mongodb asynchronous

我正忙于为报告系统开发端点。 Node 异步给我带来了问题,尽管我不想强制它同步。

我们正在使用 MongoDB 和 Mongoose。我必须通过集合 A 查询正则表达式,然后对于返回的每个文档,查询多个包含的文档以填充要返回的 JSON 对象/数组。

我可以对大部分数据使用populate,除了最终的循环查询,这是异步启动并提前返回我的报告的地方。有没有一种优雅的方法可以做到这一点?或者我应该拆分成一个不同的函数并多次调用它以遵守 functions should only do 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;
        });

这里的问题是逻辑/异步。没有语法,因此是半伪代码。

如有任何帮助或建议,我们将不胜感激。

最佳答案

您需要熟悉 promises 以及一般的异步。 因为您要返回一个数组,所以这就是您要获得的值。

在处理异步时您有几个选择,但在您的情况下,您想要查看两个解决方案:

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

请注意,我不会返回东西,而是将它传递给回调。

您可以使用高阶函数来清理此类问题。

// 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

// 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 不接受回调,而是返回 promise 。

关于javascript - Node.js 循环异步调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41953092/

相关文章:

JavaScript 生成的 div 标 checkout 现在其他元素后面 - 设置了 z-index

node.js - 如何修复错误 "net::ERR_SSL_SERVER_CERT_BAD_FORMAT"将 vuejs 和 nodejs 与 https 和 express 一起使用时

python - 安装 Node.js 和更新 Python

javascript - p-queue 使用多线程吗?

javascript - collection.find().stream() 和 collection.find().batchSize(100).stream() 的区别?

java - 关于 mongodb capped collection 的困惑

php - 如何从按日期排序的mongodb输出数据集合

javascript - 我有四个菜单按钮,但通过 jQuery 只有一个弹出窗口,我可以概括还是需要四个不同的窗口?

javascript - ajax返回Html

javascript - 尽管传递了 JSLInt,但输入仍意外结束