什麼是Promise
Promise 是非同步程式設計的一種解決方案。ES6中已經提供了原生
Promise
物件。一個
Promise
物件會處於以下幾種狀態(fulfilled,rejected兩種狀態一旦確定後不會改變):
待定(pending): 初始狀態,既沒有被兌現,也沒有被拒絕。
已兌現(fulfilled): 意味著操作成功完成。
已拒絕(rejected): 意味著操作失敗。
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
相關方法和一些簡單用法,歡迎留言交流。