2016-07-28 66 views
1

我从E6 Promises开始。我非常喜欢它们,但是有一个关于错误处理的重要概念,我不明白,并且希望能够澄清一些问题。ES6承诺错误没有按预期冒泡

假设以下简单的函数返回一个承诺:

function promiseString(str, timeout, doResolve) { 
     return new Promise((resolve, reject) => { 
      setTimeout(() => { 
       if (doResolve) { 
        resolve(str); 
       } else { 
        reject(new Error("Rejecting " + str)); 
       } 
      }, timeout); 
     }); 
    } 

这是很简单的,只是返回传递给它的字符串承诺,并承诺原因有待解决或拒绝(基于第三个参数)在“超时”毫秒。

我完全可以预期如下消耗这样的:

  promiseString("One", 100, true) 
       .then((str) => { console.log("First then is " + str); return promiseString(str + " two", 100, true); }) 
       .then((str) => { console.log("Second then is " + str); return promiseString(str + " three", 100, true); }) 
       .then((str) => console.log(str)) 
       .catch((err) => console.error(err)); 

如果改变任何在这条产业链的调用的第三个参数,从“真”到“假”,如预期的那样引起了我的错误并发送到console.error()。

不过,现在想象一下以下(同样愚蠢的)功能,构建一个充满希望的对象:

function DoublePromiser(str1, str2, doResolve) { 
     this.promise = new Promise((resolve, reject) => { 
      promiseString(str1, 100, doResolve) 
       .then((s1) => promiseString(s1 + str2, 100, doResolve)) 
       .then((s2) => resolve(s2)); 
     }); 
    } 

现在我消耗这段代码如下,一切都解决,并没有拒绝试想一下,(doResolve设为真):

  var dp = new DoublePromiser("Big", "Promise", true); 
      dp.promise 
       .then((s) => console.log("DoublePromise: " + s)) 
       .catch((err)=>console.log("I did catch: ", err.message)); 

正如所预料的,我在控制台看到以下内容:

DoublePromise: BigPromise

不过,现在我改变消费密码,设置doResolve为“假”(这将导致我的诺言常规拒绝):因为我是如何错误应该“冒泡”,我希望了解

  var dp = new DoublePromiser("Big", "Promise", false); 
      dp.promise 
       .then((s) => console.log("DoublePromise: " + s)) 
       .catch((err)=>console.log("I did catch: ", err.message)); 

控制台登录如下:

I did catch: Rejecting Big

但它没有。相反,控制台显示未捕获的错误:

Uncaught (in promise) Error: Rejecting Big

我只得到我期望(和希望)如果我添加一个catch在DoublePromiser链的结尾,就像这样:

function DoublePromiser(str1, str2, doResolve) { 
     this.promise = new Promise((resolve, reject) => { 
      promiseString(str1, 100, doResolve) 
       .then((s1) => promiseString(s1 + str2, 100, doResolve)) 
       .then((s2) => resolve(s2)) 
       .catch((err) => reject(err)); // ADDING THIS TO MAKE IT WORK 
     }); 
    } 

现在我得到了我所期望的,错误并未被捕获。但是,这似乎与错误冒起来的整个想法相反,并且为了重新拒绝同样的错误而发现错误似乎很奇怪。

我错过了一个简单的工作方式吗?

我错过了一些基本概念?

+0

这些100%的理论问题使用不构成实际问题的代码,在堆栈溢出方面效果不佳。 – jfriend00

+0

另外,你正在使用承诺构造函数反模式:https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns – jfriend00

+0

@estus - 我编辑了他们的标题。 – jfriend00

回答

3

您正在使用承诺构造函数反模式。不要将现有的承诺包装在自己做出的另一个承诺中,因为这只会让你做很多额外的工作来使事情正常工作,因为大多数人不会正确地做这些额外的工作,它也很容易出现编程错误。只需返回你已有的承诺。

更改此:

function DoublePromiser(str1, str2, doResolve) { 
    this.promise = new Promise((resolve, reject) => { 
     promiseString(str1, 100, doResolve) 
      .then((s1) => promiseString(s1 + str2, 100, doResolve)) 
      .then((s2) => resolve(s2)) 
      .catch((err) => reject(err)); // ADDING THIS TO MAKE IT WORK 
    }); 
} 

这样:

function DoublePromiser(str1, str2, doResolve) { 
    return promiseString(str1, 100, doResolve) 
     .then((s1) => promiseString(s1 + str2, 100, doResolve)); 
} 

而且,当时只是把它作为一个功能:

DoublePromiser("Big", "Promise", false).then(...); 

回顾:你近ALW ays想要从.then()处理程序返回内部承诺,因为这可以使嵌套错误向上传播,并且还可以正确链接/序列异步操作。

而且,您希望避免在现有承诺周围包装新的承诺,因为您可以链接到和/或返回已有的承诺。

此外,要知道,如果你做一个.catch(),将“处理”被拒绝的承诺并返回一个新的非拒绝承诺,继续从那里的承诺链,除非.catch()处理程序中返回一个拒绝承诺或抛出一个例外。所以,这样的:

p.catch((err) => console.log(err)).then(() => console.log("chain continues")) 

会很乐意这样做既console.log()语句,因为.catch()“处理”的承诺,因此承诺链愉快地继续。


正如我在以前的评论说,这100%的理论讨论是很难得到什么你真的想完成(我们猜测,真正的问题是什么),而不在如何一个20页教程承诺工作涵盖了很多东西。如果你发布了一个你正试图用这种技术解决的现实世界问题,并且我们可以通过几行代码和几段解释来展示/解释这样做的最佳方式,这会更好。

+0

非常感谢您在这方面的工作。明天我会通过你的答案。这个“真实世界”版本非常庞大而且很难想象,它很难想象为StackOverflow简洁地描述。它与使用IndexedDb创建SAAS系统的缓存系统有关,但却是一个相当复杂的缓存系统。我花了很长一段时间将“真实世界”的问题拖到这个“假世界”节选中。 我们目前的设计确实需要我们通过构造函数创建一个对象,但我会尝试将您的概念用于下一个承诺并查看我的位置。 –

+0

而....这就是为什么我爱StackOverflow。在阅读了许多规范文档和教程并获得这个“主要”权利后,便士终于抛弃了“反模式”和嵌套。在构造函数中做同样的事情也可以。完善。谢谢。 –

+0

@StephanGolux - 太好了。构造函数的问题是它不能返回一个promise。但是,你可以做你正在做的事情,它坚持实例数据中的承诺,并在调用构造函数后获取它。虽然这对我来说看起来很奇怪,但也许是因为你没有透露任何其他目的是否让它成为对象/构造函数。 – jfriend00