2016-07-28 41 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