一 前言
本文主要對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
長什麼樣,但對它用到的
resolve
、
reject
、
then
、
catch
想必還不了解。下面我們一步步學習。
Promise
對象代表一個未完成、但預計将來會完成的操作。
它有以下三種狀态:
-
:初始值,不是fulfilled,也不是rejectedpending
-
:代表操作成功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
來建構一個
Promise
。
Promise
接受一個「函數」作為參數,該函數的兩個參數分别是
resolve
和
reject
。這兩個函數就是就是「回調函數」,由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
變為
fulfilled
和
rejected
時的回調函數(第二個參數非必選)。這兩個函數都接受Promise對象傳出的值作為參數。
簡單來說,
then
就是定義
resolve
和
reject
函數的,其
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添加
onFulfilled
和
onRejected
回調,并傳回的是一個新的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狀态都變為
,p的狀态才會變為fulfilled
,并将三個promise傳回的結果,按參數的順序(而不是fulfilled
的順序)存入數組,傳給p的回調函數,如例3.8。resolved
- 當p1, p2, p3其中之一狀态變為
,p的狀态也會變為rejected
,并把第一個被rejected
的promise的傳回值,傳給p的回調函數,如例3.9。reject
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中有一個執行個體的狀态發生改變(變為
fulfilled
或
rejected
),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狀态變成
resolved
或
rejected
,狀态不會再改變,這和建立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的流程:
- 使用
或者它的快捷方式new Promise(fn)
、Promise.resolve()
,傳回一個promise對象Promise.reject()
- 在
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狀态變為
resove
或
reject
,就凝固了,不會再改變
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
就先介紹到這邊了,比較基礎,有不足的地方歡迎指出,有更好的也歡迎補充~