天天看點

ES6 - 初探 Promise

首先,推薦一波Promise入門視訊課程:​​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、同步

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

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

上面的例子即同步模式,其中的while是一個死循環,它會阻塞程序,是以第三句console不會執行。 

同步模式比較簡單,也較容易編寫。但問題也顯而易見,如果請求的時間較長,而阻塞了後面代碼的執行,體驗是很不好的。是以對于一些耗時的操作,異步模式則是更好的選擇。

2.1.2、異步

下面就來看看異步模式。 

異步模式,即與同步模式相反,可以一起執行多個任務,函數調用後不會立即傳回執行的結果,如果任務A需要等待,可先執行任務B,等到任務A結果傳回後再繼續回調。 

最常見的異步模式就數定時器了,我們來看看以下的例子。

/* 例2.2 */
setTimeout(function() {
    console.log('taskA, asynchronous');
}, 0);
console.log('taskB, synchronize');
//while(true);

-------ouput-------
taskB, synchronize
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.

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

/* 例2.3 */
/******************同步回調******************/
var fun1 = function(callback) {
    //do something
    console.log("before callback");
    (callback && typeof(callback) === 'function') && callback();
    console.log("after callback");
}
var fun2 = function(param) {
    //do something
    var start = new Date();
    while((new Date() - start) < 3000) { //delay 3s
    }
    console.log("I'm callback");
}
fun1(fun2);

-------output--------
before callback
//after 3s
I’m callback
after callback      

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

上一小節中​

​setTimeout​

​就是常見的異步回調,另外常見的異步回調即ajax請求。

/* 例2.4 */
/******************異步回調******************/
function request(url, param, successFun, errorFun) {
    $.ajax({
        type: 'GET',
        url: url,
        param: param,
        async: true,    //預設為true,即異步請求;false為同步請求
        success: successFun,
        error: errorFun
    });
}
request('test.html', '', function(data) {
    //請求成功後的回調函數,通常是對請求回來的資料進行處理
    console.log('請求成功啦, 這是傳回的資料:', data);
},function(error) {
    console.log('sorry, 請求失敗了, 這是失敗資訊:', error);
});      

2.2、為什麼使用Promise

說完了以上基本概念,我們就可以繼續學習​

​Promise​

​了。

上面提到,​

​Promise​

​​對象是用于異步操作的。既然我們可以使用異步回調來進行異步操作,為什麼還要引入一個​

​Promise​

​​新概念,還要花時間學習它呢?不要着急,下面就來談談​

​Promise​

​的過人之處。

我們先看看下面的demo,利用​

​Promise​

​改寫例2.4的異步回調。

/* 例2.5 */
function sendRequest(url, param) {
    return new Promise(function (resolve, reject) {
        request(url, param, resolve, reject);
    });
}

sendRequest('test.html', '').then(function(data) {
    //異步操作成功後的回調
    console.log('請求成功啦, 這是傳回的資料:', data);
}, function(error) {
    //異步操作失敗後的回調
    console.log('sorry, 請求失敗了, 這是失敗資訊:', error);
});      

這麼一看,并沒有什麼差別,還比上面的異步回調複雜,得先建立Promise再定義其回調。其實,​

​Promise​

​的真正強大之處在于它的多重鍊式調用,可以避免層層嵌套回調。如果我們在第一次ajax請求後,還要用它傳回的結果再次請求呢?

/* 例2.6 */
request('test1.html', '', function(data1) {
    console.log('第一次請求成功, 這是傳回的資料:', data1);
    request('test2.html', data1, function (data2) {
        console.log('第二次請求成功, 這是傳回的資料:', data2);
        request('test3.html', data2, function (data3) {
            console.log('第三次請求成功, 這是傳回的資料:', data3);
            //request... 繼續請求
        }, function(error3) {
            console.log('第三次請求失敗, 這是失敗資訊:', error3);
        });
    }, function(error2) {
        console.log('第二次請求失敗, 這是失敗資訊:', error2);
    });
}, function(error1) {
    console.log('第一次請求失敗, 這是失敗資訊:', error1);
});      

以上出現了多層回調嵌套,有種暈頭轉向的感覺。這也就是我們常說的厄運回調金字塔(Pyramid of Doom),程式設計體驗十分不好。而使用​

​Promise​

​​,我們就可以利用​

​then​

​進行「鍊式回調」,将異步操作以同步操作的流程表示出來。

/* 例2.7 */
sendRequest('test1.html', '').then(function(data1) {
    console.log('第一次請求成功, 這是傳回的資料:', data1);
    return sendRequest('test2.html', data1);
}).then(function(data2) {
    console.log('第二次請求成功, 這是傳回的資料:', data2);
    return sendRequest('test3.html', data2);
}).then(function(data3) {
    console.log('第三次請求成功, 這是傳回的資料:', data3);
}).catch(function(error) {
    //用catch捕捉前面的錯誤
    console.log('sorry, 請求失敗了, 這是失敗資訊:', error);
});      

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

​Promise​

​的學習。

三、Promise的基本用法

3.1、基本用法

上一小節我們認識了​

​promise​

​​長什麼樣,但對它用到的​

​resolve​

​​、​

​reject​

​​、​

​then​

​​、​

​catch​

​想必還不了解。下面我們一步步學習。

​Promise​

​對象代表一個未完成、但預計将來會完成的操作。

它有以下三種狀态:

  • ​pending​

    ​:初始值,不是fulfilled,也不是rejected
  • ​fulfilled​

    ​:代表操作成功
  • ​rejected​

    ​:代表操作失敗

​Promise​

​​有兩種狀态改變的方式,既可以從​

​pending​

​​轉變為​

​fulfilled​

​​,也可以從​

​pending​

​​轉變為​

​rejected​

​​。一旦狀态改變,就「凝固」了,會一直保持這個狀态,不會再發生變化。當狀态發生變化,​

​promise.then​

​綁定的函數就會被調用。

注意:​

​Promise​

​一旦建立就會「立即執行」,無法取消。這也是它的缺點之一。

下面就通過例子進一步講解。

/* 例3.1 */
//建構Promise
var promise = new Promise(function (resolve, reject) {
    if (/* 異步操作成功 */) {
        resolve(data);
    } else {
        /* 異步操作失敗 */
        reject(error);
    }
});      

類似建構對象,我們使用​

​new​

​​來建構一個​

​Promise​

​​。​

​Promise​

​​接受一個「函數」作為參數,該函數的兩個參數分别是​

​resolve​

​​和​

​reject​

​。這兩個函數就是就是「回調函數」,由JavaScript引擎提供。

​resolve​

​函數的作用:在異步操作成功時調用,并将異步操作的結果,作為參數傳遞出去; 

​reject​

​函數的作用:在異步操作失敗時調用,并将異步操作報出的錯誤,作為參數傳遞出去。

Promise執行個體生成以後,可以用​

​then​

​​方法指定​

​resolved​

​​狀态和​

​reject​

​狀态的回調函數。

/* 接例3.1 */
promise.then(onFulfilled, onRejected);

promise.then(function(data) {
  // do something when success
}, function(error) {
  // do something when failure
});      

​then​

​​方法會傳回一個Promise。它有兩個參數,分别為Promise從​

​pending​

​​變為​

​fulfilled​

​​和​

​rejected​

​時的回調函數(第二個參數非必選)。這兩個函數都接受Promise對象傳出的值作為參數。

簡單來說,​

​then​

​​就是定義​

​resolve​

​​和​

​reject​

​​函數的,其​

​resolve​

​參數相當于:

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

而建立Promise中的'resolve(data)',則相當于執行resolveFun函數。

Promise建立後就會立即執行。而​

​then​

​方法中指定的回調函數,将在目前腳本所有同步任務執行完才會執行。如下例:

/* 例3.2 */
var promise = new Promise(function(resolve, reject) {
  console.log('before resolved');
  resolve();
  console.log('after resolved');
});

promise.then(function() {
  console.log('resolved');
});

console.log('outer');

-------output-------
before resolved
after resolved
outer
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)​

​的别名,用于指定發生錯誤時的回調函數。

/* 例3.3 */
promise.then(function(data) {
    console.log('success');
}).catch(function(error) {
    console.log('error', error);
});

/*******等同于*******/
promise.then(function(data) {
    console.log('success');
}).then(undefined, function(error) {
    console.log('error', error);
});      
/* 例3.4 */
var promise = new Promise(function (resolve, reject) {
    throw new Error('test');
});
/*******等同于*******/
var promise = new Promise(function (resolve, reject) {
    reject(new Error('test'));
});

//用catch捕獲
promise.catch(function (error) {
    console.log(error);
});
-------output-------
Error: test      

從上例可以看出,​

​reject​

​方法的作用,等同于抛錯。

promise對象的錯誤,會一直向後傳遞,直到被捕獲。即錯誤總會被下一個​

​catch​

​​所捕獲。​

​then​

​​方法指定的回調函數,若抛出錯誤,也會被下一個​

​catch​

​​捕獲。​

​catch​

​​中也能抛錯,則需要後面的​

​catch​

​來捕獲。

/* 例3.5 */
sendRequest('test.html').then(function(data1) {
    //do something
}).then(function (data2) {
    //do something
}).catch(function (error) {
    //處理前面三個Promise産生的錯誤
});      

上文提到過,promise狀态一旦改變就會凝固,不會再改變。是以promise一旦​

​fulfilled​

​​了,再抛錯,也不會變為​

​rejected​

​​,就不會被​

​catch​

​了。

/* 例3.6 */
var promise = new Promise(function(resolve, reject) {
  resolve();
  throw 'error';
});

promise.catch(function(e) {
   console.log(e);      //This is never called
});      

如果沒有使用​

​catch​

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

/* 例3.7 */
var promise = new Promise(function (resolve, reject) {
    resolve(x);
});
promise.then(function (data) {
    console.log(data);
});      
ES6 - 初探 Promise
ES6 - 初探 Promise
ES6 - 初探 Promise

如圖所示,隻有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。
/* 例3.8 */
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 3000, "first");
});
var p2 = new Promise(function (resolve, reject) {
    resolve('second');
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "third");
}); 

Promise.all([p1, p2, p3]).then(function(values) { 
  console.log(values); 
});

-------output-------
//約 3s 後
["first", "second", "third"]      
/* 例3.9 */
var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, "one"); 
}); 
var p2 = new Promise((resolve, reject) => { 
  setTimeout(reject, 2000, "two"); 
});
var p3 = new Promise((resolve, reject) => {
  reject("three");
});

Promise.all([p1, p2, p3]).then(function (value) {
    console.log('resolve', value);
}, function (error) {
    console.log('reject', error);    // => reject three
});

-------output-------
reject three      

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

/* 例3.10 */
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();

Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + 'ms');
    console.log(values);
});
-------output-------
133ms       //不一定,但大于128ms
[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的回調函數。

/* 例3.11 */
var p1 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
    console.log('resolve', value); 
}, function(error) {
    //not called
    console.log('reject', error); 
});
-------output-------
resolve two

var p3 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "three");
});
var p4 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 100, "four"); 
});

Promise.race([p3, p4]).then(function(value) {
    //not called
    console.log('resolve', value);              
}, function(error) {
    console.log('reject', error); 
});
-------output-------
reject four      

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

/* 例3.12 */
var fastPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('fastPromise');
        resolve('resolve fastPromise');
    }, 100);
});
var slowPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('slowPromise');
        resolve('resolve slowPromise');
    }, 1000);
});
// 第一個promise變為resolve後程式停止
Promise.race([fastPromise, slowPromise]).then(function (value) {
    console.log(value);    // => resolve fastPromise
});
-------output-------
fastPromise
resolve fastPromise
slowPromise     //仍會執行      

.resolve()

文法:

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

它可以看做​

​new Promise()​

​的快捷方式。

Promise.resolve('Success');

/*******等同于*******/
new Promise(function (resolve) {
    resolve('Success');
});      

這段代碼會讓這個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對象。

/* 例3.15 */
var p1 = Promise.resolve({ 
    then: function (resolve, reject) { 
        resolve("this is an thenable object!");
    }
});
console.log(p1 instanceof Promise);     // => true

p1.then(function(value) {
    console.log(value);     // => this is an thenable object!
  }, function(e) {
    //not called
});      

再看下面兩個例子,無論是在什麼時候抛異常,隻要promise狀态變成​

​resolved​

​​或​

​rejected​

​,狀态不會再改變,這和建立promise是一樣的。

/* 例3.16 */
//在回調函數前抛異常
var p1 = { 
    then: function(resolve) {
      throw new Error("error");
      resolve("Resolved");
    }
};

var p2 = Promise.resolve(p1);
p2.then(function(value) {
    //not called
}, function(error) {
    console.log(error);       // => Error: error
});

//在回調函數後抛異常
var p3 = { 
    then: function(resolve) {
        resolve("Resolved");
        throw new Error("error");
    }
};

var p4 = Promise.resolve(p3);
p4.then(function(value) {
    console.log(value);     // => Resolved
}, function(error) {
    //not called
});      

.reject()

文法:Promise.reject(reason)      

這個方法和上述的​

​Promise.resolve()​

​​類似,它也是​

​new Promise()​

​的快捷方式。

Promise.reject(new Error('error'));

/*******等同于*******/
new Promise(function (resolve, reject) {
    reject(new Error('error'));
});      

這段代碼會讓這個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了錯誤

/* 例4.1 */
function taskA() {
    console.log(x);
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask() {
    console.log("Final Task");
}
var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask);
    
-------output-------
Catch Error: A or B,ReferenceError: x is not defined
Final Task      
ES6 - 初探 Promise

根據例4.1的輸出結果及流程圖,可以看出,A抛錯時,會按照 taskA → onRejected → finalTask這個流程來處理。A抛錯後,若沒有對它進行處理,如例3.7,狀态就會維持​

​rejected​

​​,taskB不會執行,直到​

​catch​

​了錯誤。

/* 例4.2 */
function taskA() {
    console.log(x);
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejectedA(error) {
    console.log("Catch Error: A", error);
}
function onRejectedB(error) {
    console.log("Catch Error: B", error);
}
function finalTask() {
    console.log("Final Task");
}
var promise = Promise.resolve();
promise
    .then(taskA)
    .catch(onRejectedA)
    .then(taskB)
    .catch(onRejectedB)
    .then(finalTask);
    
-------output-------
Catch Error: A ReferenceError: x is not defined
Task B
Final Task      

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

情景3:每次調用​

​then​

​​都會傳回一個新建立的promise對象,而​

​then​

​内部隻是傳回的資料

/* 例4.3 */
//方法1:對同一個promise對象同時調用 then 方法
var p1 = new Promise(function (resolve) {
    resolve(100);
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 100

//方法2:對 then 進行 promise chain 方式進行調用
var p2 = new Promise(function (resolve) {
    resolve(100);
});
p2.then(function (value) {
    return value * 2;
}).then(function (value) {
    return value * 2;
}).then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 400      

第一種方法中,​

​then​

​的調用幾乎是同時開始執行的,且傳給每個then的value都是100,這種方法應當避免。方法二才是正确的鍊式調用。

是以容易出現下面的錯誤寫法:

/* 例4.4 */
function badAsyncCall(data) {
    var promise = Promise.resolve(data);
    promise.then(function(value) {
        //do something
        return value + 1;
    });
    return promise;
}
badAsyncCall(10).then(function(value) {
   console.log(value);          //想要得到11,實際輸出10
});
-------output-------
10      

正确的寫法應該是:

/* 改寫例4.4 */
function goodAsyncCall(data) {
    var promise = Promise.resolve(data);
    return promise.then(function(value) {
        //do something
        return value + 1;
    });
}
goodAsyncCall(10).then(function(value) {
   console.log(value);
});
-------output-------
11      

情景4:在異步回調中抛錯,不會被​

​catch​

​到

// Errors thrown inside asynchronous functions will act like uncaught errors
var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

promise.catch(function(e) {
  console.log(e);       //This is never called
});      

情景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      

五、結語

  • ​​https://developer.mozilla.org...​​
  • ​​JavaScript Promise迷你書(中文版)​​
  • ​​http://es6.ruanyifeng.com/#do...​​