带承诺的多路流的正确模式

Correct pattern for multiway flows with Promises

本文关键字:模式 多路 承诺      更新时间:2023-09-26

所以我一直在玩承诺在过去的几天里,只是试图转换一些项目,使用承诺,但不止几次我遇到了这个问题。

在阅读文章和教程时,一切看起来都很流畅和干净:

getDataFromDB()
.then(makeCalculatons)
.then(getDataFromDB)
.then(serveToClient)

但现实并非如此。
程序有很多if条件,这些条件改变了整个流程:

getDataFromCache(data).then(function(result){
    if(result){
        return result;
    }else{
        return getDataFromDB();
    }
}).then(function(result){
    if(result){
        serveToClient() //this does not return a promise, so undefined returned...
    }else{
        return getDataFromWebService(); //this does return a promise, 
    }
}).then(function(result){
    //i dont want to reach here if i already serveToClient()...
    //so i basically have to check "if(result)" for all next thens
    if(result){
       //do more stuff
    }
}).then(...

我有两个主要问题:

  1. 我发现自己在then回调上添加了很多if条件。
  2. 我仍然进入下一个then回调,即使我已经完成(serveToClient)


我是否遵循了正确的模式?

您无法避免if语句,因为这是逻辑流所必需的。如果你不想在if的一个部分继续承诺链,你将不得不分支你的控制流。因此,如果在第二个.then()处理程序的某些部分,您不想继续到第三个.then()处理程序,那么您需要像这样分支逻辑,并将后续的.then()处理程序放入第二个.then()处理程序中,在它们自己的逻辑分支中。

你不能只是继续顶级分支,因为在主链中中止未来.then()逻辑的唯一方法是拒绝承诺(你可能不想这样做)或在每个.then()处理程序中添加另一个if检查来决定是否应该跳过它(恶心)。

所以,你可以这样划分逻辑:

getDataFromCache().then(function(result){
    if(!result) {
        return getDataFromDB()
    } else {
        return result;
    }
}).then(function(result){
    // branch promise chain here into two separate branches
    if(result){
        // do not continue the promise chain here
        // call a synchronous operation
        serveToClient();
    } else {
        // continue promise chain here
        return getDataFromWebService().then(function(result) {
            if(result){
               //do more stuff
            }
        }).then(...);    // you can continue the promise chain here
    }
}).catch(function(err) {
    // process any errors here
});

你可能会发现这些答案很有用:

理解javascript承诺;栈和链接

promise.then.then和promise.then有区别吗?promise.then


供参考,你可以重新组织上面的代码,使其更简洁一点,像这样:

getDataFromCache().then(function(result) {
    if (result)
        serveToClient();
    } else {
        return getDataFromWebService().then(function(result) {
            if(result){
               //do more stuff
            }
        }).then(...);    // you can continue the promise chain here
    }
}).catch(function(err) {
    // process any errors here
});

另一个答案解释了分支,但你也要求"平滑和干净"。

你可以使用ES6的箭头函数:

getDataFromCache()
  .then(result => result || getDataFromDB())
  .then(result => result ? serveToClient() : getDataFromWebService()
    .then(result => { /* Won't reach here if serveToClient */ }))
  .then(() => { /* can continue promise chain here */ })
  .catch(e => console.log(e));

请注意缩进的.thengetDataFromWebService()上,并在尾部看到两个))。这很好地反映了同步分支。

您可以使用ES8 async/await(现在可以在Chrome Canary和Firefox Nightly中使用!):

try {
  if (await getDataFromCache() || await getDataFromDB()) {
    serveToClient();
  } else {
    let result = await getDataFromWebService();
    /* Won't reach here if serveToClient */
  }
  /* can continue here */
} catch (e) {
  // process any errors here
}

后者提供了完全的控制,就好像事情是同步的一样,只要它在async函数中。