ES6之Promise用法詳解


一 前言

本文主要對ES6的Promise進行一些入門級的介紹。要想學習一個知識點,肯定是從三個方面出發,what、why、how。下面就跟着我一步步學習吧~

二 什么是Promise

首先是what。那么什么是Promise呢? 
以下是MDN對Promise的定義

The Promise object is used for asynchronous computations. A Promise represents a single asynchronous operation that hasn't completed yet, but is expected in the future.

譯文:Promise對象用於異步操作,它表示一個尚未完成且預計在未來完成的異步操作。

那么什么是異步操作?在學習promise之前需要把這個概念搞明白,下面將抽離一章專門介紹。

2.1 同步與異步

我們知道,JavaScript的執行環境是「單線程」。 
所謂單線程,是指JS引擎中負責解釋和執行JavaScript代碼的線程只有一個,也就是一次只能完成一項任務,這個任務執行完后才能執行下一個,它會「阻塞」其他任務。這個任務可稱為主線程。 
但實際上還有其他線程,如事件觸發線程、ajax請求線程等。

這也就引發了同步和異步的問題。

2.1.1 同步

同步模式,即上述所說的單線程模式,一次只能執行一個任務,函數調用后需等到函數執行結束,返回執行的結果,才能進行下一個任務。如果這個任務執行的時間較長,就會導致「線程阻塞」。

1 /* 例2.1 */
2 var x = true;
3 while(x);
4 console.log("don't carry out");    //不會執行

上面的例子即同步模式,其中的while是一個死循環,它會阻塞進程,因此第三句console不會執行。 
同步模式比較簡單,也較容易編寫。但問題也顯而易見,如果請求的時間較長,而阻塞了后面代碼的執行,體驗是很不好的。因此對於一些耗時的操作,異步模式則是更好的選擇。

2.1.2 異步

下面就來看看異步模式。 
異步模式,即與同步模式相反,可以一起執行多個任務,函數調用后不會立即返回執行的結果,如果任務A需要等待,可先執行任務B,等到任務A結果返回后再繼續回調。 
最常見的異步模式就數定時器了,我們來看看以下的例子。

 1 /* 例2.2 */
 2 setTimeout(function() {
 3     console.log('taskA, asynchronous');
 4 }, 0);
 5 console.log('taskB, synchronize');
 6 //while(true);
 7 
 8 -------ouput-------
 9 taskB, synchronize
10 taskA, asynchronous

我們可以看到,定時器延時的時間明明為0,但taskA還是晚於taskB執行。這是為什么呢?由於定時器是異步的,異步任務會在當前腳本的所有同步任務執行完才會執行。如果同步代碼中含有死循環,即將上例的注釋去掉,那么這個異步任務就不會執行,因為同步任務阻塞了進程。

2.1.3 回調函數

提起異步,就不得不談談回調函數了。上例中,setTimeout里的function便是回調函數。可以簡單理解為:(執行完)回(來)調(用)的函數。
以下是WikiPedia對於callback的定義。

In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time.

可以看出,回調函數是一段可執行的代碼段,它以「參數」的形式傳遞給其他代碼,在其合適的時間執行這段(回調函數)的代碼。

WikiPedia同時提到

The invocation may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback.

也就是說,回調函數不僅可以用於異步調用,一般同步的場景也可以用回調。在同步調用下,回調函數一般是最后執行的。而異步調用下,可能一段時間后執行或不執行(未達到執行的條件)。

 1 /* 例2.3 */
 2 /******************同步回調******************/
 3 var fun1 = function(callback) {
 4     //do something
 5     console.log("before callback");
 6     (callback && typeof(callback) === 'function') && callback();
 7     console.log("after callback");
 8 }
 9 var fun2 = function(param) {
10     //do something
11     var start = new Date();
12     while((new Date() - start) < 3000) { //delay 3s
13     }
14     console.log("I'm callback");
15 }
16 fun1(fun2);
17 
18 -------output--------
19 before callback
20 //after 3s
21 I’m callback
22 after callback

由於是同步回調,會阻塞后面的代碼,如果fun2是個死循環,后面的代碼就不執行了。

上一小節中setTimeout就是常見的異步回調,另外常見的異步回調即ajax請求。

 1 /* 例2.4 */
 2 /******************異步回調******************/
 3 function request(url, param, successFun, errorFun) {
 4     $.ajax({
 5         type: 'GET',
 6         url: url,
 7         param: param,
 8         async: true,    //默認為true,即異步請求;false為同步請求
 9         success: successFun,
10         error: errorFun
11     });
12 }
13 request('test.html', '', function(data) {
14     //請求成功后的回調函數,通常是對請求回來的數據進行處理
15     console.log('請求成功啦, 這是返回的數據:', data);
16 },function(error) {
17     console.log('sorry, 請求失敗了, 這是失敗信息:', error);
18 });

2.2 為什么使用Promise

說完了以上基本概念,我們就可以繼續學習Promise了。
上面提到,Promise對象是用於異步操作的。既然我們可以使用異步回調來進行異步操作,為什么還要引入一個Promise新概念,還要花時間學習它呢?不要着急,下面就來談談Promise的過人之處。
我們先看看下面的demo,利用Promise改寫例2.4的異步回調。

 1 /* 例2.5 */
 2 function sendRequest(url, param) {
 3     return new Promise(function (resolve, reject) {
 4         request(url, param, resolve, reject);
 5     });
 6 }
 7 
 8 sendRequest('test.html', '').then(function(data) {
 9     //異步操作成功后的回調
10     console.log('請求成功啦, 這是返回的數據:', data);
11 }, function(error) {
12     //異步操作失敗后的回調
13     console.log('sorry, 請求失敗了, 這是失敗信息:', error);
14 });

這么一看,並沒有什么區別,還比上面的異步回調復雜,得先新建Promise再定義其回調。其實,Promise的真正強大之處在於它的多重鏈式調用,可以避免層層嵌套回調。如果我們在第一次ajax請求后,還要用它返回的結果再次請求呢?

 1 /* 例2.6 */
 2 request('test1.html', '', function(data1) {
 3     console.log('第一次請求成功, 這是返回的數據:', data1);
 4     request('test2.html', data1, function (data2) {
 5         console.log('第二次請求成功, 這是返回的數據:', data2);
 6         request('test3.html', data2, function (data3) {
 7             console.log('第三次請求成功, 這是返回的數據:', data3);
 8             //request... 繼續請求
 9         }, function(error3) {
10             console.log('第三次請求失敗, 這是失敗信息:', error3);
11         });
12     }, function(error2) {
13         console.log('第二次請求失敗, 這是失敗信息:', error2);
14     });
15 }, function(error1) {
16     console.log('第一次請求失敗, 這是失敗信息:', error1);
17 });

以上出現了多層回調嵌套,有種暈頭轉向的感覺。這也就是我們常說的厄運回調金字塔(Pyramid of Doom),編程體驗十分不好。而使用Promise,我們就可以利用then進行「鏈式回調」,將異步操作以同步操作的流程表示出來。

 1 /* 例2.7 */
 2 sendRequest('test1.html', '').then(function(data1) {
 3     console.log('第一次請求成功, 這是返回的數據:', data1);
 4 }).then(function(data2) {
 5     console.log('第二次請求成功, 這是返回的數據:', data2);
 6 }).then(function(data3) {
 7     console.log('第三次請求成功, 這是返回的數據:', data3);
 8 }).catch(function(error) {
 9     //用catch捕捉前面的錯誤
10     console.log('sorry, 請求失敗了, 這是失敗信息:', error);
11 });

是不是明顯清晰很多?孰優孰略也無需多說了吧~下面就讓我們真正進入Promise的學習。

三 Promise的基本用法

3.1 基本用法

上一小節我們認識了promise長什么樣,但對它用到的resolverejectthencatch想必還不理解。下面我們一步步學習。

Promise對象代表一個未完成、但預計將來會完成的操作。
它有以下三種狀態:

  • pending:初始值,不是fulfilled,也不是rejected
  • fulfilled:代表操作成功
  • rejected:代表操作失敗

Promise有兩種狀態改變的方式,既可以從pending轉變為fulfilled,也可以從pending轉變為rejected。一旦狀態改變,就「凝固」了,會一直保持這個狀態,不會再發生變化。當狀態發生變化,promise.then綁定的函數就會被調用。
注意:Promise一旦新建就會「立即執行」,無法取消。這也是它的缺點之一。
下面就通過例子進一步講解。

 1 /* 例3.1 */
 2 //構建Promise
 3 var promise = new Promise(function (resolve, reject) {
 4     if (/* 異步操作成功 */) {
 5         resolve(data);
 6     } else {
 7         /* 異步操作失敗 */
 8         reject(error);
 9     }
10 });

類似構建對象,我們使用new來構建一個PromisePromise接受一個「函數」作為參數,該函數的兩個參數分別是resolvereject。這兩個函數就是就是「回調函數」,由JavaScript引擎提供。

resolve函數的作用:在異步操作成功時調用,並將異步操作的結果,作為參數傳遞出去; 
reject函數的作用:在異步操作失敗時調用,並將異步操作報出的錯誤,作為參數傳遞出去。

Promise實例生成以后,可以用then方法指定resolved狀態和reject狀態的回調函數。

1 /* 接例3.1 */
2 promise.then(onFulfilled, onRejected);
3 
4 promise.then(function(data) {
5   // do something when success
6 }, function(error) {
7   // do something when failure
8 });

then方法會返回一個Promise。它有兩個參數,分別為Promise從pending變為fulfilledrejected時的回調函數(第二個參數非必選)。這兩個函數都接受Promise對象傳出的值作為參數
簡單來說,then就是定義resolvereject函數的,其resolve參數相當於:

1 function resolveFun(data) {
2     //data為promise傳出的值
3 }

而新建Promise中的'resolve(data)',則相當於執行resolveFun函數。
Promise新建后就會立即執行。而then方法中指定的回調函數,將在當前腳本所有同步任務執行完才會執行。如下例:

 1 /* 例3.2 */
 2 var promise = new Promise(function(resolve, reject) {
 3   console.log('before resolved');
 4   resolve();
 5   console.log('after resolved');
 6 });
 7 
 8 promise.then(function() {
 9   console.log('resolved');
10 });
11 
12 console.log('outer');
13 
14 -------output-------
15 before resolved
16 after resolved
17 outer
18 resolved

由於resolve指定的是異步操作成功后的回調函數,它需要等所有同步代碼執行后才會執行,因此最后打印'resolved',這個和例2.2是一樣的道理。

3.2 基本API

.then()

語法:Promise.prototype.then(onFulfilled, onRejected) 

對promise添加onFulfilledonRejected回調,並返回的是一個新的Promise實例(不是原來那個Promise實例),且返回值將作為參數傳入這個新Promise的resolve函數。

因此,我們可以使用鏈式寫法,如上文的例2.7。由於前一個回調函數,返回的還是一個Promise對象(即有異步操作),這時后一個回調函數,就會等待該Promise對象的狀態發生變化,才會被調用。

.catch()

語法:Promise.prototype.catch(onRejected) 

該方法是.then(undefined, onRejected)的別名,用於指定發生錯誤時的回調函數。

 1 /* 例3.3 */
 2 promise.then(function(data) {
 3     console.log('success');
 4 }).catch(function(error) {
 5     console.log('error', error);
 6 });
 7 
 8 /*******等同於*******/
 9 promise.then(function(data) {
10     console.log('success');
11 }).then(undefined, function(error) {
12     console.log('error', error);
13 });
 1 /* 例3.4 */
 2 var promise = new Promise(function (resolve, reject) {
 3     throw new Error('test');
 4 });
 5 /*******等同於*******/
 6 var promise = new Promise(function (resolve, reject) {
 7     reject(new Error('test'));
 8 });
 9 
10 //用catch捕獲
11 promise.catch(function (error) {
12     console.log(error);
13 });
14 -------output-------
15 Error: test

從上例可以看出,reject方法的作用,等同於拋錯。

promise對象的錯誤,會一直向后傳遞,直到被捕獲。即錯誤總會被下一個catch所捕獲。then方法指定的回調函數,若拋出錯誤,也會被下一個catch捕獲。catch中也能拋錯,則需要后面的catch來捕獲。

1 /* 例3.5 */
2 sendRequest('test.html').then(function(data1) {
3     //do something
4 }).then(function (data2) {
5     //do something
6 }).catch(function (error) {
7     //處理前面三個Promise產生的錯誤
8 });

上文提到過,promise狀態一旦改變就會凝固,不會再改變。因此promise一旦fulfilled了,再拋錯,也不會變為rejected,就不會被catch了。

1 /* 例3.6 */
2 var promise = new Promise(function(resolve, reject) {
3   resolve();
4   throw 'error';
5 });
6 
7 promise.catch(function(e) {
8    console.log(e);      //This is never called
9 });

如果沒有使用catch方法指定處理錯誤的回調函數,Promise對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應(Chrome會拋錯),這是Promise的另一個缺點。

1 /* 例3.7 */
2 var promise = new Promise(function (resolve, reject) {
3     resolve(x);
4 });
5 promise.then(function (data) {
6     console.log(data);
7 });

 

 

 

 

如圖所示,只有Chrome會拋錯,且promise狀態變為rejected,Firefox和Safari中錯誤不會被捕獲,也不會傳遞到外層代碼,最后沒有任何輸出,promise狀態也變為rejected

.all()

語法:Promise.all(iterable) 

該方法用於將多個Promise實例,包裝成一個新的Promise實例。

var p = Promise.all([p1, p2, p3]);

Promise.all方法接受一個數組(或具有Iterator接口)作參數,數組中的對象(p1、p2、p3)均為promise實例(如果不是一個promise,該項會被用Promise.resolve轉換為一個promise)。它的狀態由這三個promise實例決定。

  • 當p1, p2, p3狀態都變為fulfilled,p的狀態才會變為fulfilled,並將三個promise返回的結果,按參數的順序(而不是 resolved的順序)存入數組,傳給p的回調函數,如例3.8。
  • 當p1, p2, p3其中之一狀態變為rejected,p的狀態也會變為rejected,並把第一個被reject的promise的返回值,傳給p的回調函數,如例3.9。
 1 /* 例3.8 */
 2 var p1 = new Promise(function (resolve, reject) {
 3     setTimeout(resolve, 3000, "first");
 4 });
 5 var p2 = new Promise(function (resolve, reject) {
 6     resolve('second');
 7 });
 8 var p3 = new Promise((resolve, reject) => {
 9   setTimeout(resolve, 1000, "third");
10 }); 
11 
12 Promise.all([p1, p2, p3]).then(function(values) { 
13   console.log(values); 
14 });
15 
16 -------output-------
17 //約 3s 后
18 ["first", "second", "third"] 

 

 1 /* 例3.9 */
 2 var p1 = new Promise((resolve, reject) => { 
 3   setTimeout(resolve, 1000, "one"); 
 4 }); 
 5 var p2 = new Promise((resolve, reject) => { 
 6   setTimeout(reject, 2000, "two"); 
 7 });
 8 var p3 = new Promise((resolve, reject) => {
 9   reject("three");
10 });
11 
12 Promise.all([p1, p2, p3]).then(function (value) {
13     console.log('resolve', value);
14 }, function (error) {
15     console.log('reject', error);    // => reject three
16 });
17 
18 -------output-------
19 reject three

這多個 promise 是同時開始、並行執行的,而不是順序執行。從下面例子可以看出。如果一個個執行,那至少需要 1+32+64+128

 1 /* 例3.10 */
 2 function timerPromisefy(delay) {
 3     return new Promise(function (resolve) {
 4         setTimeout(function () {
 5             resolve(delay);
 6         }, delay);
 7     });
 8 }
 9 var startDate = Date.now();
10 
11 Promise.all([
12     timerPromisefy(1),
13     timerPromisefy(32),
14     timerPromisefy(64),
15     timerPromisefy(128)
16 ]).then(function (values) {
17     console.log(Date.now() - startDate + 'ms');
18     console.log(values);
19 });
20 -------output-------
21 133ms       //不一定,但大於128ms
22 [1,32,64,128]

.race()

語法:Promise.race(iterable) 

該方法同樣是將多個Promise實例,包裝成一個新的Promise實例。

var p = Promise.race([p1, p2, p3]);

Promise.race方法同樣接受一個數組(或具有Iterator接口)作參數。當p1, p2, p3中有一個實例的狀態發生改變(變為fulfilledrejected),p的狀態就跟着改變。並把第一個改變狀態的promise的返回值,傳給p的回調函數。

 1 /* 例3.11 */
 2 var p1 = new Promise(function(resolve, reject) { 
 3     setTimeout(reject, 500, "one"); 
 4 });
 5 var p2 = new Promise(function(resolve, reject) { 
 6     setTimeout(resolve, 100, "two"); 
 7 });
 8 
 9 Promise.race([p1, p2]).then(function(value) {
10     console.log('resolve', value); 
11 }, function(error) {
12     //not called
13     console.log('reject', error); 
14 });
15 -------output-------
16 resolve two
17 
18 var p3 = new Promise(function(resolve, reject) { 
19     setTimeout(resolve, 500, "three");
20 });
21 var p4 = new Promise(function(resolve, reject) { 
22     setTimeout(reject, 100, "four"); 
23 });
24 
25 Promise.race([p3, p4]).then(function(value) {
26     //not called
27     console.log('resolve', value);              
28 }, function(error) {
29     console.log('reject', error); 
30 });
31 -------output-------
32 reject four

在第一個promise對象變為resolve后,並不會取消其他promise對象的執行,如下例

 1 /* 例3.12 */
 2 var fastPromise = new Promise(function (resolve) {
 3     setTimeout(function () {
 4         console.log('fastPromise');
 5         resolve('resolve fastPromise');
 6     }, 100);
 7 });
 8 var slowPromise = new Promise(function (resolve) {
 9     setTimeout(function () {
10         console.log('slowPromise');
11         resolve('resolve slowPromise');
12     }, 1000);
13 });
14 // 第一個promise變為resolve后程序停止
15 Promise.race([fastPromise, slowPromise]).then(function (value) {
16     console.log(value);    // => resolve fastPromise
17 });
18 -------output-------
19 fastPromise
20 resolve fastPromise
21 slowPromise     //仍會執行

.resolve()

語法:

 
Promise.resolve(value); Promise.resolve(promise); Promise.resolve(thenable);

它可以看做new Promise()的快捷方式。

1 Promise.resolve('Success');
2 
3 /*******等同於*******/
4 new Promise(function (resolve) {
5     resolve('Success');
6 });

這段代碼會讓這個Promise對象立即進入resolved狀態,並將結果success傳遞給then指定的onFulfilled回調函數。由於Promise.resolve()也是返回Promise對象,因此可以用.then()處理其返回值。

/* 例3.13 */
Promise.resolve('success').then(function (value) {
    console.log(value);
});
-------output-------
Success
/* 例3.14 */
//Resolving an array
Promise.resolve([1,2,3]).then(function(value) {
  console.log(value[0]);    // => 1
});

//Resolving a Promise
var p1 = Promise.resolve('this is p1');
var p2 = Promise.resolve(p1);
p2.then(function (value) {
    console.log(value);     // => this is p1
});

Promise.resolve()的另一個作用就是將thenable對象(即帶有then方法的對象)轉換為promise對象。

 1 /* 例3.15 */
 2 var p1 = Promise.resolve({ 
 3     then: function (resolve, reject) { 
 4         resolve("this is an thenable object!");
 5     }
 6 });
 7 console.log(p1 instanceof Promise);     // => true
 8 
 9 p1.then(function(value) {
10     console.log(value);     // => this is an thenable object!
11   }, function(e) {
12     //not called
13 });

再看下面兩個例子,無論是在什么時候拋異常,只要promise狀態變成resolvedrejected,狀態不會再改變,這和新建promise是一樣的。

 1 /* 例3.16 */
 2 //在回調函數前拋異常
 3 var p1 = { 
 4     then: function(resolve) {
 5       throw new Error("error");
 6       resolve("Resolved");
 7     }
 8 };
 9 
10 var p2 = Promise.resolve(p1);
11 p2.then(function(value) {
12     //not called
13 }, function(error) {
14     console.log(error);       // => Error: error
15 });
16 
17 //在回調函數后拋異常
18 var p3 = { 
19     then: function(resolve) {
20         resolve("Resolved");
21         throw new Error("error");
22     }
23 };
24 
25 var p4 = Promise.resolve(p3);
26 p4.then(function(value) {
27     console.log(value);     // => Resolved
28 }, function(error) {
29     //not called
30 });

.reject()

語法:Promise.reject(reason) 

這個方法和上述的Promise.resolve()類似,它也是new Promise()的快捷方式。

1 Promise.reject(new Error('error'));
2 
3 /*******等同於*******/
4 new Promise(function (resolve, reject) {
5     reject(new Error('error'));
6 });

這段代碼會讓這個Promise對象立即進入rejected狀態,並將錯誤對象傳遞給then指定的onRejected回調函數。

四 Promise常見問題

經過上一章的學習,相信大家已經學會使用Promise
總結一下創建promise的流程:

  1. 使用new Promise(fn)或者它的快捷方式Promise.resolve()Promise.reject(),返回一個promise對象
  2. fn中指定異步的處理
    處理結果正常,調用resolve
    處理結果錯誤,調用reject

如果使用ES6的箭頭函數,將會使寫法更加簡單清晰。

這一章節,將會用例子的形式,以說明promise使用過程中的注意點及容易犯的錯誤。

情景1:reject 和 catch 的區別

  • promise.then(onFulfilled, onRejected)
    onFulfilled中發生異常的話,在onRejected中是捕獲不到這個異常的。
  • promise.then(onFulfilled).catch(onRejected)
    .then中產生的異常能在.catch中捕獲

一般情況,還是建議使用第二種,因為能捕獲之前的所有異常。當然了,第二種的.catch()也可以使用.then()表示,它們本質上是沒有區別的,.catch === .then(null, onRejected)


情景2:如果在then中拋錯,而沒有對錯誤進行處理(即catch),那么會一直保持reject狀態,直到catch了錯誤

 1 /* 例4.1 */
 2 function taskA() {
 3     console.log(x);
 4     console.log("Task A");
 5 }
 6 function taskB() {
 7     console.log("Task B");
 8 }
 9 function onRejected(error) {
10     console.log("Catch Error: A or B", error);
11 }
12 function finalTask() {
13     console.log("Final Task");
14 }
15 var promise = Promise.resolve();
16 promise
17     .then(taskA)
18     .then(taskB)
19     .catch(onRejected)
20     .then(finalTask);
21     
22 -------output-------
23 Catch Error: A or B,ReferenceError: x is not defined
24 Final Task

 

 

根據例4.1的輸出結果及流程圖,可以看出,A拋錯時,會按照 taskA → onRejected → finalTask這個流程來處理。A拋錯后,若沒有對它進行處理,如例3.7,狀態就會維持rejected,taskB不會執行,直到catch了錯誤。

 1 /* 例4.2 */
 2 function taskA() {
 3     console.log(x);
 4     console.log("Task A");
 5 }
 6 function taskB() {
 7     console.log("Task B");
 8 }
 9 function onRejectedA(error) {
10     console.log("Catch Error: A", error);
11 }
12 function onRejectedB(error) {
13     console.log("Catch Error: B", error);
14 }
15 function finalTask() {
16     console.log("Final Task");
17 }
18 var promise = Promise.resolve();
19 promise
20     .then(taskA)
21     .catch(onRejectedA)
22     .then(taskB)
23     .catch(onRejectedB)
24     .then(finalTask);
25     
26 -------output-------
27 Catch Error: A ReferenceError: x is not defined
28 Task B
29 Final Task

將例4.2與4.1對比,在taskA后多了對A的處理,因此,A拋錯時,會按照A會按照 taskA → onRejectedA → taskB → finalTask這個流程來處理,此時taskB是正常執行的。


情景3:每次調用then都會返回一個新創建的promise對象,而then內部只是返回的數據

 1 /* 例4.3 */
 2 //方法1:對同一個promise對象同時調用 then 方法
 3 var p1 = new Promise(function (resolve) {
 4     resolve(100);
 5 });
 6 p1.then(function (value) {
 7     return value * 2;
 8 });
 9 p1.then(function (value) {
10     return value * 2;
11 });
12 p1.then(function (value) {
13     console.log("finally: " + value);
14 });
15 -------output-------
16 finally: 100
17 
18 //方法2:對 then 進行 promise chain 方式進行調用
19 var p2 = new Promise(function (resolve) {
20     resolve(100);
21 });
22 p2.then(function (value) {
23     return value * 2;
24 }).then(function (value) {
25     return value * 2;
26 }).then(function (value) {
27     console.log("finally: " + value);
28 });
29 -------output-------
30 finally: 400

第一種方法中,then的調用幾乎是同時開始執行的,且傳給每個then的value都是100,這種方法應當避免。方法二才是正確的鏈式調用。
因此容易出現下面的錯誤寫法:

 1 /* 例4.4 */
 2 function badAsyncCall(data) {
 3     var promise = Promise.resolve(data);
 4     promise.then(function(value) {
 5         //do something
 6         return value + 1;
 7     });
 8     return promise;
 9 }
10 badAsyncCall(10).then(function(value) {
11    console.log(value);          //想要得到11,實際輸出10
12 });
13 -------output-------
14 10

正確的寫法應該是:

 1 /* 改寫例4.4 */
 2 function goodAsyncCall(data) {
 3     var promise = Promise.resolve(data);
 4     return promise.then(function(value) {
 5         //do something
 6         return value + 1;
 7     });
 8 }
 9 goodAsyncCall(10).then(function(value) {
10    console.log(value);
11 });
12 -------output-------
13 11

情景4:在異步回調中拋錯,不會被catch

 1 // Errors thrown inside asynchronous functions will act like uncaught errors
 2 var promise = new Promise(function(resolve, reject) {
 3   setTimeout(function() {
 4     throw 'Uncaught Exception!';
 5   }, 1000);
 6 });
 7 
 8 promise.catch(function(e) {
 9   console.log(e);       //This is never called
10 });

情景5: promise狀態變為resovereject,就凝固了,不會再改變

console.log(1);
new Promise(function (resolve, reject){
    reject();
    setTimeout(function (){
        resolve();            //not called
    }, 0);
}).then(function(){
    console.log(2);
}, function(){
    console.log(3);
});
console.log(4);

-------output-------
1
4
3

五 結語

關於promise就先介紹到這邊了,比較基礎,有不足的地方歡迎指出,有更好的也歡迎補充~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM