返回承诺链中的引用数据

Posted

技术标签:

【中文标题】返回承诺链中的引用数据【英文标题】:Returning referenced data in promise chain 【发布时间】:2017-11-27 08:53:06 【问题描述】:

假设我需要找一些学生:

var promise = Student.find(..).exec();

然后我需要访问单独引用它们的文档并将它们嵌入返回:

promise.then(function(students) 
  var promises = [];
  students.forEach(function(student) 
    var groupPromise = Group.find( studentRef: student._id ).exec();
    groupPromise.then(function(group) 
      ...
      student.embed = group;
      return student;
    );
    promises.push(groupPromise);
  );
  return Promise.all(promises);
).then(function(result) 
   // expect result to be array of students, with  embed: group 
   // instead result is array of groups
);

我在弄清承诺方面已经走了很长一段路,但我错过了一些东西。我希望groupPromise.then(...)Promise.all(...) 可以被评估之前执行。显然不是这样,我需要一种方法来确保每个student 都嵌入了正确的group

【问题讨论】:

在 *** 上,您不会将答案放入您的问题中。问题就是问题。答案就是答案。您可以编辑您的问题以澄清问题。如果您最终使用没有其他人在他们的答案中向您展示如何做的解决方案,您甚至可以做出自己的答案。但是,你没有把答案放在你的问题中。请删除解决方案信息。是的,这与其他一些系统不同。 *** 有自己的做事方式。 @jfriend00 完成 【参考方案1】:

基本上只有一件事不对:

groupPromise.then(function(group) 
  ...
  student.embed = group;
  return student;
)
.then(function(student)
  //this is what you want
);

groupPromise.then(function(group)
  //this is what you do
);

groupPromise 始终解析为 group。可以将解析为 student 的 groupPromise.then Promise 添加到您的 Promise.all 中:

promises.push(groupPromise.then(function(group) 
  ...
  student.embed = group;
  return student;
));

我将如何做整个事情:

promise.then(function(students) 
    return Promise.all(students.map(function(student) 
        return Group.find( studentRef: student._id ).exec().then(function(group) 
            ...
            student.embed = group;
            return student;
        );
    ));
).then(function(result) 
// expect result to be array of students, with  embed: group 
);

【讨论】:

【参考方案2】:

您放入数组并传递给Promise.all() 的promise 需要是groupPromise.then() 的返回结果,而不是groupPromise 本身。因为您使用的是groupPromise,所以您看不到groupPromise.then() 的结果。

请记住,每个 .then() 都会返回一个 承诺,并且正是该新承诺跟踪 .then() 结果。您没有将新的承诺传递给Promise.all(),因此您的结果没有显示.then() 内部发生的事情的结果。

改成这样:

promise.then(function(students) 
  var promises = [];
  students.forEach(function(student) 
    var p = Group.find( studentRef: student._id ).exec().then(function(group) 
      ...
      student.embed = group;
      return student;
    );
    promises.push(p);
  );
  return Promise.all(promises);
).then(function(result) 
   // result is array of students, with  embed: group 
);

仅供参考,当使用 .map() 而不是 .forEach() 时,这会更加精简:

promise.then(function(students) 
    return Promise.all(students.map(function(student) 
        return Group.find( studentRef: student._id ).exec().then(function(group) 
            ...
            student.embed = group;
            return student;
        );
    ));
).then(function(result) 
   // result is array of students, with  embed: group 
);

而且,使用来自Bluebird's promise library 的Promise.map() 更简单:

promise.then(function(students) 
    return Promise.map(students, function(student) 
        return Group.find( studentRef: student._id ).exec().then(function(group) 
            ...
            student.embed = group;
            return student;
        );
    );
).then(function(result) 
   // result is array of students, with  embed: group 
);    

【讨论】:

【参考方案3】:

我发现从变量声明中移动承诺是可行的:

var promise = Student.find(..).exec();
promise.then(function(students) 
  var promises = [];
  students.forEach(function(student) 
    promises.push(
      Group.find( studentRef: student._id ).exec()
        .then(function(group) 
          ...
          student.embed = group;
          return student;
        )
    );
  );
  return Promise.all(promises);
).then(function(result) 
  // now results is an array of students with  embed: group        
);

我必须承认,我不知道它为什么有效。有人吗?


编辑:找到解决方案。 正如各位聪明人所指出的,重要的是返回承诺的哪一部分。推入promises 数组的promise 需要是.then() 返回的promise。这些方法实际上只是这个的句法类似物,但我认为它们可能对未来的读者有所启发。

方法一 - 完全推送 promise.then():

var promises = [];
students.forEach(function(student) 
  promises.push(Group.find().exec().then(function(group) 
     student.embed
     return student;
  );
);
return Promise.all(promises); 

不是特别漂亮。

方法二 - 将 promise.then() 赋值给另一个变量:

var promises = [];
students.forEach(function(student) 
  var groupPromise = Group.find().exec();
  var embedPromise = groupPromise.then(function(group) 
     student.embed
     return student;
  );
  promises.push(embedPromise);
);
return Promise.all(promises);

比方法一漂亮一点,但阅读起来并不容易。

方法三 - 正如 jfriend00 和 Jonas w 所建议的,.map() 很优雅:

promise.then(function(students) 
  return Promise.all(students.map(function(student) 
    return Group.find( studentRef: student._id ).exec().then(function(group) 
      ...
      student.embed = group;
      return student;
    );
  ));
).then(function(result) 
  // expect result to be array of students, with  embed: group 
);

【讨论】:

不是从变量声明中移动来修复它,而是你链接了 .then 看看你push。在这种情况下,您推送then 的结果,在另一种情况下,您推送原始承诺,而不是then 返回的承诺。 保证承诺顺序的是你如何链接它们,而不是你创建它们的位置

以上是关于返回承诺链中的引用数据的主要内容,如果未能解决你的问题,请参考以下文章

承诺链中的返回值没有被调用

Firestore 交叉引用返回一个承诺对象,无法访问文档值

如何编写测试用例来覆盖承诺链中所有嵌套的“then”回调

PromiseKit 分支承诺

将 Sequelize Promise 嵌入到 javascript async/await 链中

承诺链中断言的捕获错误