1. 程式人生 > 其它 >JavaScript Promise返回值詳解

JavaScript Promise返回值詳解

JavaScript Promise返回值詳解
Promise回顧
Promise回撥函式返回非Promise值
Promise回撥函式返回Promise物件
Promise回撥函式中丟擲錯誤
總結
Promise回顧
Promise物件是JavaScript ES6標準中一個重要的內容,它是為了非同步而生的,相比於經典的回撥函式寫法,在處理大量非同步任務時使用Promise鏈在可讀性上會遠遠優於回撥函式導致的回撥地獄,本文建立在讀者已經對Promise有所瞭解的情況下,如果你還不瞭解Promise,可以參考MDN上的這篇文章。

下面我們先來簡要回顧一下Promise物件的性質,如果你已經足夠熟悉,請前往下一節內容。

Promise有三種狀態,如果用Promise()構造器建立一個Promise物件,當被建立時,它的狀態是pending,如果一個Promise物件的resolve方法被呼叫,它的狀態會變成fulfilled,而如果一個Promise物件的reject方法被呼叫,它的狀態會變成rejected。此外,還有兩種初始化Promise物件的方法,分別是Promise.resolve方法和Promise.reject方法,前者會直接返回一個狀態為fulfilled的Promise物件而後者會直接返回一個狀態為rejected的Promise物件。

在一個Promise鏈中,如果一個Promise狀態變成了fulfilled,它會自動在Promise鏈中向下尋找,直到發現一個then方法,並執行其中的第一個引數函式,而如果一個Promise的狀態變成了rejected,它會在Promise鏈中向下尋找,直到發現一個帶有兩個引數的then方法並執行它的第二個引數函式或發現一個catch方法並執行它的引數函式。

要知道,Promise.prototype.then和Promise.prototype.catch都會返回一個Promise物件,這是Promise鏈能生效的關鍵,這篇文章討論的重點就是這兩個方法的返回值。

注意:為了增強可讀性,本文中涉及到Promise.prototype.then都只傳入一個引數,作為fulfilled狀態的回撥函式,rejected狀態的回撥函式在Promise.prototype.catch中定義。

Promise回撥函式返回非Promise值
當一個Promise.prototype.then方法被呼叫時,且在回撥函式中返回的值是一個非Promise物件時,它會生成一個狀態為fulfilled的新的Promise物件,並把該返回值傳入下一個回撥函式,看下面這個例子:

Promise.resolve().then(function() {
  return 'Hello World';
})
.then(function(value) {
  console.log(`fulfilled: ${value}`); // 'fulfilled: Hello World'
})
.catch(function(value) {
  console.log(`rejected: ${value}`);
});

我們先用Promise.resolve生成了一個fulfilled狀態的Promise物件以便呼叫then方法,在它的回撥函式中我們返回了一個字串Hello World,可以看到Promise鏈中第二個then方法的回撥函式被呼叫,打印出了fulfilled: Hello World,說明前一個回撥函式的返回值被傳入,且在Promise鏈的上一環節返回的新Promise狀態為fulfilled。

當一個Promise.prototype.catch方法被呼叫,且在回撥函式中返回的值是一個非Promise物件,它仍然會生成一個狀態為fulfilled的新的Promise物件,並把該返回值傳入下一個回撥函式,相當於錯誤已經被捕獲,看下面這個例子:

Promise.reject().catch(function() {
  return 'Hello World';
})
.then(function(value) {
  console.log(`fulfilled: ${value}`); // 'fulfilled: Hello World'
})
.catch(function(value) {
  console.log(`rejected: ${value}`);
})

我們先用Promise.reject生成了一個rejected狀態的Promise物件,這時第一個catch方法觸發,在它的回撥函式裡返回了字串Hello World,此後第一個then方法觸發,打印出了fulfilled: Hello World,說明前一個回撥函式的返回值被傳入,且在Promise鏈的上一環節返回的新Promise狀態為fulfilled。

你可以動手試試上面的例子,自己嘗試著修改一些程式碼並檢視返回值,有助於對這一節的內容有更深的理解。

Promise回撥函式返回Promise物件
如果Promise的回撥函式中返回的是Promise,那麼無論觸發的是Promise鏈中的then方法還是catch方法,新生成的Promise物件的狀態都直接取決於回撥函式中返回的Promise物件的狀態,傳進下一個回撥函式的值也取決於這個被返回的Promise物件,讓我們看下面幾個例子:

Promise.resolve().then(function() {
  return Promise.resolve('Hello World');
})
.then(function(value) {
  console.log(`fulfilled: ${value}`); // 'fulfilled: Hello World'
})
.catch(function(value) {
  console.log(`rejected: ${value}`);
});
Promise.resolve().then(function() {
  return Promise.reject('Hello World');
})
.then(function(value) {
  console.log(`fulfilled: ${value}`);
})
.catch(function(value) {
  console.log(`rejected: ${value}`); // 'rejected: Hello World'
});
Promise.reject().catch(function() {
  return Promise.resolve('Hello World');
})
.then(function(value) {
  console.log(`fulfilled: ${value}`); // 'fulfilled: Hello World'
})
.catch(function(value) {
  console.log(`rejected: ${value}`);
});
Promise.reject().catch(function() {
  return Promise.reject('Hello World');
})
.then(function(value) {
  console.log(`fulfilled: ${value}`);
})
.catch(function(value) {
  console.log(`rejected: ${value}`); // 'rejected: Hello World'
});

你可以動手試試上面的例子來更深刻地感受一下返回Promise物件與返回其他值時的不同。

通過這幾個例子可以看到,如果回撥函式中返回Promise物件,無論是then方法還是catch方法生成的Promise物件都直接取決於回撥函式中的這個Promise物件。

Promise回撥函式中丟擲錯誤
如果Promise的回撥函式中丟擲了一個錯誤,則會生成一個狀態為rejected的Promise,並將這個錯誤作為引數傳給Promise鏈的下一個回撥函式,看下面兩個例子:

Promise.resolve().then(function() {
  throw new Error('Oops!');
})
.then(function(value) {
  console.log('fulfilled');
  console.log(value.message);
})
.catch(function(value) {
  console.log('rejected'); // 'rejected'
  console.log(value.message); // 'Oops!'
});
Promise.reject().catch(function() {
  throw new Error('Oops!');
})
.then(function(value) {
  console.log('fulfilled');
  console.log(value.message);
})
.catch(function(value) {
  console.log('rejected'); // 'rejected'
  console.log(value.message); // 'Oops!'
})

你可以動手試試來感受一下這種情況下丟擲錯誤對生成的Promise物件的影響。

可以看到,這種情況下catch的回撥函式被執行,說明丟擲錯誤後,返回的Promise狀態是rejected,並且傳入Promise鏈下一環節的值是這個錯誤物件。

總結
當Promise的回撥函式返回非Promise物件的值時,then和catch都生成一個狀態為fulfilled的Promise物件,並把該返回值傳入Promise鏈的下一環節。
當Promise的回撥函式返回值為Promise物件時,生成的Promise物件的狀態由被返回的Promise物件決定,傳入Promise鏈下一環節的值也由這個被返回的Promise決定。
當Promise的回撥函式中丟擲錯誤時,then和catch都生成一個狀態為rejected的Promise物件,並把丟擲的錯誤物件傳入Promise鏈的下一環節。