使用 Promises 的多路流的正确模式

Correct pattern for multiway flows with Promises

所以最近几天我一直在玩promises,只是想转换一些项目,使用promises,但是我遇到了好几次这个问题。

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

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

但实际上并非如此。
程序有很多 "if conditions" 改变了整个流程:

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(...

我有 2 个主要问题:

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


我遵循的模式是否正确?

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

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

因此,您可以像这样分支逻辑:

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

您可能会发现这些其他答案很有用:


仅供参考,您可以将上面的代码重组为更简洁的代码:

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

另一个答案解释了分支,但你也要求 "smooth and clean"。

你可以使用 ES6 arrow functions:

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 and 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 functions