【问题标题】:How to resolve a different promise in each case of a switch block and pass their results to the same function?如何在 switch 块的每种情况下解决不同的承诺并将它们的结果传递给相同的函数?
【发布时间】:2019-01-20 23:35:12
【问题描述】:

假设我想在一个switch块的各个case块中执行不同的promise链,并最终通过res.end()将结果返回给客户端,像这样:

app.post('/endpoint',function(req,res){
    var reqValue=req.body.value;
    var resValue="initial value";
    switch(reqValue){
         case 'a':
             someObj.action_a()
             .then(result=>{
                 resValue=result.id;
             });
             break;
         case 'b':
             someObj.action_b()
             .then(result=>{
                 resValue=result.id;
             });
             break;
         default:
             resValue="default";
    }
    doSomethingElse();
    res.end(resValue);
});

最终发生的事情是resValue 返回为"initial value",这是有道理的,因为case 块中的异步函数在执行到达resValue 之前没有更新resValue。我可以将 post-switch 代码移动到这样的承诺解决方案中:

         case 'a':
             someObj.action_a()
             .then(result=>{
                 resValue=result.id;
                 doSomethingElse();
                 res.end(resValue);
             });
             break;
         case 'b':
             someObj.action_b()
             .then(result=>{
                 resValue=result.id;
                 doSomethingElse();
                 res.end(resValue);
             });
             break;
         default:
             resValue="default";
             doSomethingElse();
             res.end(resValue);

但这是重复代码,因此维护起来更具挑战性。有没有更好的方法让这些switch 介导的承诺都以相同的res.end() 结束?

【问题讨论】:

  • treat this block of code as synchronous - 处理异步代码的危险方法 - use async await 没有其余部分如何:p

标签: javascript node.js promise


【解决方案1】:

您可以像这样使用单个变量来保持所需 resValue 的承诺

app.post('/endpoint',function(req,res){
    let reqValue=req.body.value;
    let p;
    switch(reqValue){
         case 'a':
             p = someObj.action_a().then(result => result.id);
             break;
         case 'b':
             p = someObj.action_b().then(result => result.id);
             break;
         default:
             // p has to be a promise, so make it one
             p = Promise.resolve("default");
    }
    p.then(resValue => {
        doSomethingElse();
        res.end(resValue);
    });
});

或者使用现代 javascript,使用 async/await

app.post('/endpoint',async function(req,res){
    let reqValue=req.body.value;
    let resValue="initial value";
    switch(reqValue){
         case 'a':
             resValue = await someObj.action_a().then(result => result.id);
             break;
         case 'b':
             resValue = await someObj.action_b().then(result => result.id);
             break;
         default:
             resValue = "default";
    }
    doSomethingElse();
    res.end(resValue);
});

【讨论】:

  • 这就是为什么复制/粘贴是一件坏事:p
  • 我不听你的指控@vdegenne - 当然我复制/粘贴然后更改 OP的代码
【解决方案2】:

如果您可以使用 JavaScript 的新功能,我建议您使用 asyncawait,因为它们易于阅读和使用,您的代码将更改为:

let resValue = "default";
switch (reqValue) {
  case 'a':
    resValue = (await someObj.action_a()).id;
    break;
  case 'b':
    resValue = (await someObj.action_b()).id;
    break;
  default:
    break;
}
doSomethingElse();
res.end(resValue);

【讨论】:

  • 你不能像那样使用await - 即你忘记表明封闭函数必须是async :p ... If you can use new features of JavaScript - OP 使用箭头函数,漂亮安全的赌注:p
  • @JaromandaX 故意省略!因为如果他要使用这种现代的 JS 语法,他肯定会遇到其他需要向自己学习的问题。不用说,我只是想指导他找到他可能感兴趣的解决方案:p
  • @JaromandaX OP uses arrow functions, pretty safe bet :p - asyncawait 是 ES2017,箭头函数是 ES2015。这不是一个非常安全的赌注。
  • @vdegenne resValue = (await someObj.action_a()).id; 将反映问题中的意图。目前,您分配的是result,而不是result.id
  • @PatrickRoberts - 没有多少人会使用一个有一个而不是另一个的环境 - 不过,有些人会挂在特别旧的 nodejs 上,所以我想这是一个公平的观点
【解决方案3】:

也许,相反,您可以创建一个通用函数,根据给定的操作类型返回数据,并在其中设置switch,并在主函数中使用简单的 async/await 来等待结果

// Made up function that switches action
// based on the type (in this case the timer on the
// setTimeout)
function doAction(type) {
  let time;
  switch(type) {
    case 'a': time = 1000; break;
    case 'b': time = 2000; break;
    case 'c': time = 300; break;
  }
  return new Promise(resolve => {
    setTimeout(() => resolve({ id: `${type}1` }), time);
  });
}

async function main(type) {
  try {

    // Pass in the type to the doAction function,
    // let that decide what to do, and await the promise
    // to resolve
    const result = await doAction(type);

    // Then just console.log or res.send the result 
    const resValue = result.id;
    console.log(resValue);
  } catch (e) {
    console.log(e);
  }
}

main('b');
main('a');
main('c');

【讨论】:

    【解决方案4】:

    如果您不能使用async/await,而必须坚持使用Promises,这是一种选择(但是,如果您有很多情况,嵌套的三元组可能会变得难看;请继续阅读以获得不同的想法):

    app.post('/endpoint', function(req, res) {
      const reqValue = req.body.value;
      (reqValue === 'a'
        ? someObj.action_a().then(({id}) => id)
        : reqValue === 'b'
          ? someObj.action_b.then(({id}) => id)
          : Promise.resolve('default'))
            .then(resValue => {
              doSomethingElse();
              res.end(resValue);
            });
    });
    

    相反,假设您将操作存储在Object

    const actions = {
      a: () => someObj.action_a();
      b: () => someObj.action_b();
      // ...
      n: () => someObj.action_n();
    };
    
    app.post('endpoint', function(req, res) {
      const action = actions[req.body.value];
      (action && action().then(({id}) => id) || Promise.resolve('default'))
        .then(resValue => {
          doSomethingElse();
          res.end(resValue);
        });
    });
    

    【讨论】:

      【解决方案5】:

      Promise.all 是一个非常方便的工具,用于跟踪所有子承诺任务

      const jobqQeue = [];
      
      switch(...) {
        case: '...': {
          jobqQeue.push(
            subPromise()
          );
        }
        case: '...': {
          jobqQeue.push(
            subPromise2()
          );
        }
        default: {
          jobqQeue.push(
            defaultOpPromise();
          );
        }
      }
      
      Promise.all(jobqQeue)
      .then(results => {
        ...
      })
      .catch(error => console.error(error));

      【讨论】:

      • 当只有一个承诺时,没有理由拥有一系列承诺:p
      猜你喜欢
      • 1970-01-01
      • 2020-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多