如何修复承诺反模式 - 幽灵承诺?

时间:2016-08-02 15:31:42

标签: javascript promise

我找到了“幽灵承诺”一词here,看起来像我的情况。

我有这样的代码:

return Q.Promise(function(resolve, reject) {
  firstFunctionThatReturnPromise()
  .then(function(firstResult) {
    _check(firstResult) ? resolve(firstResult) : return secondFunctionThatReturnPromise();
  })
  .then(function(secondResult) {
    console.log(secondResult);
    return thirdFunctionThatReturnPromise(secondResult);
  })
  .then(function(thirdResult) {
    resolve(thirdResult);
  })
  .catch(function(e) {
    reject(e)
  });
});

问题是,即使_check返回true,它仍会继续执行console.log命令(导致undefined)。

如果_check返回false,则按预期工作。

所以我的问题是:

  • 如果上述行为正常吗?
  • 如果有更优雅的方式来处理这种情况?

更新1:许多人质疑为什么我使用Q.Promise而不是直接返回结果。这是因为这是一个通用函数,由其他函数共享。

// Usage in other functions
genericFunction()
.then(function(finalResult) {
   doSomething(finalResult);
})
.catch(function(err) {
   handleError(err);
});

3 个答案:

答案 0 :(得分:2)

首先,我没有理由围绕这一点做出新的承诺。你的操作已经返回了promises,因此将error prone anti-pattern重新包装在一个新的承诺中。

其他人已经说过,.then()处理程序有这些选择:

  1. 它可以返回一个将传递给下一个.then()处理程序的结果。不返回任何内容会将undefined传递给下一个.then()处理程序。
  2. 它可以返回一个promise,其解析后的值将被传递给下一个.then()处理程序,否则被拒绝的值将被传递给下一个拒绝处理程序。
  3. 它可能会拒绝当前的承诺。
  4. .then()处理程序无法告诉promise链有条件地跳过除拒绝之外的一些.then()个处理程序。

    因此,如果您想根据某些条件逻辑分支您的承诺,那么您需要根据您的分支逻辑实际嵌套您的.then()处理程序

    a().then(function(result1) {
        if (result1) {
            return result1;
        } else {
            // b() is only executed here in this conditional
            return b().then(...);
        }
    }).then(function(result2) {
        // as long as no rejection, this is executed for both branches of the above conditional
        // result2 will either be result1 or the resolved value of b()
        // depending upon your conditional
    })
    

    因此,当您想要分支时,您可以创建一个新的嵌套链,使您可以根据条件分支控制发生的事情。

    使用你的伪代码,它看起来像这样:

    firstFunctionThatReturnPromise().then(function (firstResult) {
        if (_check(firstResult)) {
            return firstResult;
        } else {
            return secondFunctionThatReturnPromise().then(function (secondResult) {
                console.log(secondResult);
                return thirdFunctionThatReturnPromise(secondResult);
            })
        }
    }).then(function (finalResult) {
        console.log(finalResult);
        return finalResult;
    }).catch(function (err) {
        console.log(err);
        throw err;
    });
    

    即使这是在genericFunction中,您仍然可以返回已有的承诺:

    function genericFunction() {
        return firstFunctionThatReturnPromise().then(function (firstResult) {
            if (_check(firstResult)) {
                return firstResult;
            } else {
                return secondFunctionThatReturnPromise().then(function (secondResult) {
                    console.log(secondResult);
                    return thirdFunctionThatReturnPromise(secondResult);
                })
            }
        }).then(function (finalResult) {
            console.log(finalResult);
            return finalResult;
        }).catch(function (err) {
            console.log(err);
            throw err;
        });
    }
    
    // usage
    genericFunction().then(...).catch(...)
    

答案 1 :(得分:1)

预期会有这种行为。当你链接.then()语句时,除了抛出错误之外,你不能提前突破链。

您的顶级承诺(由Q.Promise()返回的承诺)在_check()之后得到解决;但实际上你有一个继续执行的内部承诺链。

根据规范,then()会返回一个承诺:https://promisesaplus.com/#point-40

您可以在Q的源代码中看到自己:https://github.com/kriskowal/q/blob/v1/q.js#L899

对于您想要的行为,您实际上需要另一个嵌套的保证链。

return Q.Promise(function(resolve, reject) {
  firstFunctionThatReturnPromise().then(function(firstResult) {
    if (_check(firstResult)) {
      resolve(firstResult);
    } else {
      return secondFunctionThatReturnPromise().then(function(secondResult) {
        console.log(secondResult);
        return thirdFunctionThatReturnPromise(secondResult);
      });
    }
  });
});

答案 2 :(得分:0)

我从未使用过Q,但承诺返回的所有内容都会转换为承诺并传递给下一个.then()。在这里,您的第一个.then()不会返回任何内容。所以它返回undefined。因此undefined包含在新Promise中并传递给下一个处理程序,您将获得secondResult == undefined

您可以在以下CodePen中查看它:http://codepen.io/JesmoDrazik/pen/xOaXKE