前端JavaScript之Promise
1、什麼是Promise
Promise
是異步編程的一種解決方案。ES6中已經提供瞭原生 Promise
對象。一個 Promise
對象會處於以下幾種狀態(fulfilled
,rejected
兩種狀態一旦確定後不會改變):
- 待定(
pending
): 初始狀態,既沒有被兌現,也沒有被拒絕。 - 已兌現(
fulfilled
): 意味著操作成功完成。 - 已拒絕(
rejected
): 意味著操作失敗。
2、基本用法
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){ // .... })
3、Promise的方法
3.1 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 })
3.2 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
3.3 Promise.prototype.finally()
finally()
方法不管 Promise
對象最後狀態如何,都會執行的操作。下面是我們使用 Promise 的一個常規結構。
promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
3.4 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){ // ... });
3.5 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();
3.6 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('所有請求完成後(包括成功失敗)執行');
3.7 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();
3.8 Promise.resolve()
Promise.resolve()
方法將現有對象轉換為 Promise
對象。等價於下面代碼:
new Promise(resolve => resolve(1))
傳入的參數不同,處理
- 參數
Promise
實例,它將不做任何修改、原封不動地返回這個實例。 - 參數
thenable
對象,它會將這個對象轉為Promise
對象,然後就立即執行thenable
對象的then()
方法。
let thenable = { then: function(resolve, reject) { resolve(1); } };
- 參數是普通值,返回一個新的
Promise
對象,狀態為resolved
。
const promise = Promise.resolve(1); promise.then(function (value) { console.log(value) // 1 });
- 無參數,直接返回一個
resolved
狀態的Promise
對象。
3.9 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
4、簡單場景
異步加載圖片:
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 })
到此這篇關於前端JavaScript
之Promise
的文章就介紹到這瞭,更多相關JavaScript Promise
內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 徹底搞懂 javascript的Promise
- 字節飛書面試promise.all實現示例
- ES6的Promise用法詳解
- JavaScript中的Promise詳解
- Promise靜態四兄弟實現示例詳解