前端-JavaScript非同步程式設計中的Promise

什麼是Promise

Promise 是非同步程式設計的一種解決方案。ES6中已經提供了原生

Promise

物件。一個

Promise

物件會處於以下幾種狀態(fulfilled,rejected兩種狀態一旦確定後不會改變):

待定(pending): 初始狀態,既沒有被兌現,也沒有被拒絕。

已兌現(fulfilled): 意味著操作成功完成。

已拒絕(rejected): 意味著操作失敗。

前端-JavaScript非同步程式設計中的Promise

promises。png

基本用法

Promise

物件是一個建構函式,用來建立

Promise

例項,它接收兩個引數

resolve

reject

resolve

的作用是將

Promise

物件的狀態從

pending

變為

fulfilled

,在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去。

reject

的作用是將

Promise

物件的狀態從

pending

變為

rejected

,在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。

const promise = new Promise(function(resolve, reject) { // 。。。 if (/* 非同步操作成功 */){ resolve(value); } else { reject(error); }});

Promise

例項生成以後,使用

then

方法分別指定

fulfilled

狀態和

rejected

狀態的回撥函式。

then

接收兩個引數,第一個是

Promise

物件的狀態變為

fulfilled

時的回撥函式,第二個是狀態變為

rejected

時的回撥函式。

catch

接收

Promise

物件的狀態變為

rejected

時的回撥函式。

promise。then(function (value){ // 。。。。},function (err){ // 。。。。 err}) promise。then(function (value){ // 。。。。})。catch(function (err){ // 。。。。})

Promise的方法

Promise。prototype。then()

then

方法是定義在原型物件

Promise。prototype

上,前面說過,它接收兩個可選引數,第一個引數是

fulfilled

狀態的回撥函式,第二個引數是

rejected

狀態的回撥函式。

then

方法返回的是一個新的

Promise

例項,方便我們採用鏈式寫法。比如

then

後面接著寫

then

,當第一個回撥函式完成以後,會將返回結果作為引數,傳入第二個回撥函式。這種鏈式方式可以很方便地指定一組按照次序呼叫的回撥函式。

loadData()。then(function (value){ return 3})。then(function (num){ console。log(“ok”, num) // 3})

Promise。prototype。catch()

catch

方法是用於指定發生錯誤時的回撥函式。如果非同步操作丟擲錯誤,狀態就會變為

rejected

,就會呼叫

catch()

方法指定的回撥函式,處理這個錯誤。

const promise = new Promise(function(resolve, reject) { throw new Error(‘unkonw error’); // 丟擲錯誤狀態變為 -> reject});const promise = new Promise(function(resolve, reject) { reject(‘unkonw error’) // 使用reject()方法將狀態變為 -> reject});promise。catch(function(error) { console。log(error);});

Promise

物件的錯誤會一直向後傳遞,直到被捕獲為止。比如下面程式碼:

catch

會捕獲

loadData

和兩個

then

裡面丟擲的錯誤。

loadData()。then(function(value) { return loadData(value);})。then(function(users) { })。catch(function(err) { // 處理前面三個Promise產生的錯誤});

如果我們不設定

catch()

,當遇到錯誤時

Promise

不會將錯誤丟擲外面,也就是不會影響外部程式碼執行。

const promise = new Promise(function(resolve, reject) { resolve(a) // ReferenceError: a is not defined});promise。then(function(value) { console。log(‘value is ’, value)});setTimeout(() => { console。log(‘code is run’) }, 1000); // code is run

Promise。prototype。finally()

finally()

方法不管 Promise 物件最後狀態如何,都會執行的操作。下面是我們使用 Promise 的一個常規結構。

promise。then(result => {···})。catch(error => {···})。finally(() => {···});

Promise。all()

Promise。all()

方法可以將多個 Promise 例項包裝成一個新的 Promise 例項返回。

const promise = Promise。all([p1, p2, p3]);

promise

狀態來依賴於“傳入的

promise

”。

只有當所有“傳入的

promise

”狀態都變成

fulfilled

,它的狀態才會變成

fulfilled

,此時“傳入的

promise

”返回值組成一個數組,傳遞給

promise

的回撥函式。

如果“傳入的

promise

”之中有一個被

rejected

,新

promise

的狀態就會變成

rejected

,此時第一個被

reject

promise

的返回值,會傳遞給

promise

的回撥函式。

const promises = [1,2,3,4]。map(function (id) { return loadData(id);});Promise。all(promises)。then(function (users) { // 。。。})。catch(function(err){ // 。。。});

Promise。race()

Promise。race()

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

Promise。race()

方法的引數與

Promise。all()

方法一樣。

const promise = Promise。race([p1, p2, p3]);

Promise。all()

Promise。race()

對比:

Promise。all()

,如果所有都執行成功則返回所有成功的

promise

值,如果有失敗則返回第一個失敗的值。

Promise。race()

,返回第一個執行完成的

promise

值,它可能是fulfilled和rejected狀態。

這兩個方法的使用場景。

場景一

,使用者登入社交網站主頁後,會同時非同步請求拉取使用者資訊,關注列表,粉絲列表,我們需要保證所有資料請求成功再進行渲染頁面,只要有一個數據不成功就會重定向頁面,這裡可以使用

Promise。all

function initUserHome() { Promise。all([ new Promise(getMe), new Promise(getFollows), new Promise(getFans)]) 。then(function(data){ // 顯示頁面 }) 。catch(function(err){ // 。。。。 重定向頁面 });};initUserHome();

場景二

,假如我們在做一個搶票軟體,雖然請求了很多賣票渠道,每次只需返回第一個執行完成的

Promise

,這裡可以使用

Promise。race

function getTicket() { Promise。race([ new Promise(postASell), new Promise(postBSell), new Promise(postCSell)]) 。then(function(data){ // 搶票成功 }) 。catch(function(err){ // 。。。。 搶票失敗,重試 });};getTicket();

Promise。allSettled()

使用

Promise。all()

時,如果有一個

Promise

失敗後,其它

Promise

不會停止執行。

const requests = [ fetch(‘/url1’), fetch(‘/url2’), fetch(‘/url3’),];try { await Promise。all(requests); console。log(‘所有請求都成功。’);} catch { console。log(‘有一個請求失敗,其他請求可能還沒結束。’);}

有的時候,我們希望等到一組非同步操作都結束了,再進行下一步操作。這時就需要使用

Promise。allSettled()

,的它引數是一個數組,陣列的每個成員都是一個 Promise 物件,返回一個新的 Promise 物件。它只有等到引數陣列的所有 Promise 物件都發生狀態變更(不管是

fulfilled

還是

rejected

),返回的 Promise 物件才會發生狀態變更。

const requests = [ fetch(‘/url1’), fetch(‘/url2’), fetch(‘/url3’),];await Promise。allSettled(requests);console。log(‘所有請求完成後(包括成功失敗)執行’);

Promise。any()

只要傳入的

Promise

有一個變成

fulfilled

狀態,新的

Promise

就會變成

fulfilled

狀態;如果所有傳入的

Promise

都變成

rejected

狀態,新的

Promise

就會變成

rejected

狀態。

Promise。any()

Promise。race()

差不多,區別在於

Promise。any()

不會因為某個

Promise

變成

rejected

狀態而結束,必須等到所有引數

Promise

變成

rejected

狀態才會結束。

回到

Promise。race()

多渠道搶票的場景,如果我們需要保證要麼有一個渠道搶票成功,要麼全部渠道都失敗,使用

Promise。any()

就顯得更合適。

function getTicket() { Promise。any([ new Promise(postASell), new Promise(postBSell), new Promise(postCSell)]) 。then(function(data){ // 有一個搶票成功 }) 。catch(function(err){ // 。。。。 所有渠道都失敗了 });};getTicket();

Promise。resolve()

Promise。resolve()

方法將現有物件轉換為

Promise

物件。等價於下面程式碼:

new Promise(resolve => resolve(1))

傳入的引數不同,處理

引數

Promise

例項,它將不做任何修改、原封不動地返回這個例項。

引數

thenable

物件,它會將這個物件轉為

Promise

物件,然後就立即執行

thenable

物件的

then()

方法。

引數是普通值,返回一個新的 Promise 物件,狀態為

resolved

無引數,直接返回一個

resolved

狀態的 Promise 物件。

Promise。reject()

Promise。reject(reason)

方法也會返回一個新的 Promise 例項,該例項的狀態為

rejected

const promise = Promise。reject(‘unkonw error’);// 相當於const promise = new Promise((resolve, reject) => reject(‘unkonw error’))promise。then(null, function (s) { console。log(s)});// unkonw error

簡單場景

非同步載入圖片:

function loadImageAsync(url) { return new Promise(function(resolve, reject) { const image = new Image(); image。onload = resolve; image。onerror = reject; image。src = url; });}

請求超時處理:

//請求function request(){ return new Promise(function(resolve, reject){ // code 。。。。 resolve(‘request ok’) })}function timeoutHandle(time){ return new Promise(function(resolve, reject){ setTimeout(function(){ reject(‘timeout’); }, time); });}Promise。race([ request(), timeoutHandle(5000)])。then(res=>{ console。log(res)})。catch(err=>{ console。log(err)// timeout})

小結

本文歸納了

Promise

相關方法和一些簡單用法,歡迎留言交流。