【问题标题】:Js : Chaining promises chains in a clear and readable wayJs:以清晰易读的方式链接承诺链
【发布时间】:2018-10-11 16:54:52
【问题描述】:

你好,溢出者,

我目前正在处理我的一个项目,使用带有 mongo 数据存储的节点服务器。我目前正在编写一些函数来为我填充数据库。
数据库中的对象示例有用户、班级、学年、学生评分等。
创建此类对象的所有功能都是这样实现的:

    function popUser(users){
        chain = [];
        users.forEach((v,i,a)=>{
            let p = new Promise(function(res,rej){
                let newU = new User(v);
                newU.save(()=>{
                    err && rej(err);
                    res();
                });
            });
            chain.push(p);
        });
        return chain
    }

在我的人口模块的其余部分,我根据我的需要调用这些函数。调用上述函数的顺序很重要,因此我不希望构造函数完全并行执行。

有了 Promises,我可以做这样的事情:

popUser(users).then(popClasses).then(...). ... .catch((err)=>{})

有了 Promises Chains,我知道我可以做到以下几点

Promise.all(usersChain).then(()=>{
    //make a new chain for Classes
    Promise.all(classesChain).then (()=>{},(err)=>{})
},(err)=>{})

我认为我们可以同意它变得很难阅读和理解,因此问题是:

有没有一种方法可以使用不同的、更易读的语法来实现相同的结果?

编辑:更清楚地说,usersChain 和 cleassesChains 是 Promises 的数组,用于创建和插入一些对象(或多个对象)到数据库中。 我无法制作单个链,因为在插入其他一些对象之后可能必须插入一些对象。

编辑:等等,我可以打电话吗

Promsie.all(populateCountersPromise).then(promise1).then(promise2).catch((err)=>{})

【问题讨论】:

  • 不确定usersChainclassesChain 是什么......? (除非classesChain 依赖于usersChain,最好使用单个Promise.all)另请注意,您的函数名称是popUser,但您正在调用popUsers
  • @CertainPerformance 我已经编辑了代码并添加了一个编辑部分!
  • 这是猫鼬,不是吗?问题应明确说明这一点,而不是“Mongo”。

标签: javascript node.js mongodb asynchronous


【解决方案1】:

代码中有不同的部分不起作用,或者不应该这样写。

如果你使用这样的快捷方式:

err && rej(err);
res();

你应该知道它们的意思,因为这等于:

if( err ) { 
  rej(err);
}
res();

所以如果发生错误,rejres 都会被调用。

从您的 popUser 返回一个 Promise 数组,因此 popUser(users).then(popClasses) 会失败,因为您不能在数组上调用 .then

你应该做的第一件事是清理你的popUser函数:

function popUser(users) {

  let promises = users.map((v, i, a) => {
    return new Promise(function(res, rej) {
      let newU = new User(v);
      newU.save(err => {
        if (err) {
          rej(err)
        } else {
          res()
        }
      });
    });
  });

  return Promies.all(promises)
}

使用.map 而不是forEachpush,因为它从一开始就清楚地表明了你所做的事情。并使用 Promies.all 从您的函数返回一个等待所有用户保存的 Promise,如果您对 popClasses 执行相同操作,您可以这样写:

popUser(users)
   .then(popClasses)
   .then(...)
   .catch((err)=>{})

如果你真的想像上一个代码sn-p那样写,那么把它改成这样:

Promise.all(usersChain)
  .then(() => Promise.all(classesChain))
  .then(() => {})
  .catch(err => {})

现在许多 API 都支持经典回调和 Promises,因此您可以进一步改进您的 popUser

function popUser(users) {
  let promises = users.map(v => new User(v).save())
  return Promies.all(promises)
}

【讨论】:

  • 非常感谢您的回答!没想到我可以返回 .all 并将其视为承诺,太棒了!我不确定第一点,但调用拒绝会成功,因此永远不会调用 resolve。
  • @FilippoBosco err && rej(err); 不会神奇地停止rej(err) 处代码的执行,因此仍会调用res()。调用res 不会覆盖拒绝状态,但这样做仍然是一个坏习惯。如果您例如,它将产生不必要的副作用。这样做类似res(new Something()),因为即使你拒绝了new Something(),也会被调用。
  • 是的,有道理我会更加小心,再次感谢您的回答
【解决方案2】:

Mongoose 回调是遗留 API。 Mongoose 长期支持 Promise,不需要 Promise。

对于并发承诺,forEach 可以替换为 map,这是后者的确切用例:

function popUser(users){
  return Promise.all(
    users.map(userData => new User(userData).save())
  );
}

async..await 可用于其余应按顺序进行承诺的情况:

try {
  await popUser(users);
  await popClasses();
  ...
} catch (err) {
  ...
}

【讨论】:

    【解决方案3】:

    为什么不在函数popUser 中返回Promise.all(chain)

    返回一个 Promise 更好地匹配 popUser 的语义,这是一个异步执行某些操作的函数。返回一个 Promise 数组是令人困惑的。

    然后你可以使用popUsers.then(popClasses).then(...). ... .catch((err)=>{}) 来排队promise

    【讨论】:

      猜你喜欢
      • 2016-06-21
      • 2016-11-09
      • 1970-01-01
      • 1970-01-01
      • 2016-06-13
      • 2018-09-19
      • 1970-01-01
      • 1970-01-01
      • 2015-01-21
      相关资源
      最近更新 更多