1. Promise 的含義 Promise 是非同步編程的一種解決方案,比傳統的解決方案 回調函數和事件更合理、更強大。 1.1 什麼是Promise 簡單來說就是一個容器,裡面保存著某個未來才會結束的事件(也就是非同步操作)的結果。從語法上來講,Promise是一個對象,從它可以獲取非同步操作的消息, ...
1. Promise 的含義
Promise 是非同步編程的一種解決方案,比傳統的解決方案--回調函數和事件更合理、更強大。
1.1 什麼是Promise
簡單來說就是一個容器,裡面保存著某個未來才會結束的事件(也就是非同步操作)的結果。從語法上來講,Promise是一個對象,從它可以獲取非同步操作的消息,它提供統一的API,各種非同步操作都可以用同樣的方法進行處理。
Promise有兩個特點:
1.1.1、對象的狀態不受外界影響。Promise對象代表一個非同步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態、任何其他操作都無法改版這個狀態。
1.1.2、一旦狀態改版,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只存在兩種可能:從 pending 變為 fulfilled 和 從 pending 變為 rejeced。只要這兩種情況發生,狀態就終止,不會再變了並一直保持這個結果。這時就稱為 resolved(已定型)。如果改版已經發生了,即使再對Promise對象添加回調函數,也會立即得到這個結果。如果你錯過了再想去監聽,是得不到結果的。
1.1.3、有了Promise對象,就可以將非同步操作以同步操作的流程顯示出來,這樣就避免了層層嵌套的回調函數。Promise對象提供統一的介面,使得控制非同步操作更加容易。
1.1.4、Promise也有一些缺點,就是無法取消 Promise,一旦建立就會立即執行,無法中途取消。如果不設置回調函數,Promise內部拋出的錯誤不會反應到外部。另外如果處於 pending 狀態時,是無法知道現在到了哪一個階段。
2. 基本用法
Promise對象是一個構造函數,用來生成Promise實例
const promise = new Promise((reolve, reject) => {
if (// 非同步操作成功) {
resolve(val)
}else{
reject(val)
}
})
Promise 構造函數接受一個函數作為參數,該函數的兩個參數分別是 resolve 和 reject。
resolve:,將Promise對象的狀態從『未完成』變為『成功』(pending => resolved),在非同步操作成功時調用,並將非同步操作的結果作為參數傳遞出去。
reject:將Promise對象的狀態從『未完成』變為『失敗』(pending => rejected),在非同步操作失敗時調用,並將非同步操作的結果作為參數傳遞出去。
Promise 實例生成以後,可以用 then 方法分別指定 resolved 狀態和 rejected 狀態的回調函數。
promise.then((val) => {
// success
},(err) => {
// failure
})
then方法可以接受兩個回調函數作為參數。(第二個函數可選,這兩個函數都接受Promise對象傳出的值作為參數)
1、第一個回調函數在Promise對象的狀態變為『resolved』時調用。
2、第二個回調函數在Promise對象的狀態變為『rejected』時調用。
實例:
function timeout(ms) {
retrun new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done')
})
}
timeout(100)
.then((v) => {
console.log(v)
})
上面代碼中,timeout方法返回一個Promise實例,表示一段時間以後才會發生的結果,過了 ms時間後,Promise狀態變為『resolved』然後就會觸發then方法綁定的回調函數。
Promise 建立後就會立即執行
let promise = new Promise((resolve, reject) => {
console.log('Promise')
resolve()
})
promise
.then(() => {
console.log('resolved')
})
console.log('hh')
// Promise
// hh
// resolved
Promise建立後立即執行,首先輸出 「Promise」然後執行promise 的then函數,然後首先執行同步任務 輸出 hh 在執行 then方法的回調函數輸出resolved
如果調用 resolve 函數和 reject 函數時帶有參數,那麼它們的參數會被傳遞給回調函數。reject函數的參數通常是Error對象的實例,表示拋出的錯誤。resolve函數的參數除了正常的值以外,還有可能是一個Promise實例。resolve實在成功的時候調用,reject是在失敗的時候調用。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
上述代碼中:p1 和 p2都是Promise的實例,但是p2的 resolve方法將 p1作為參數,即一個非同步操作的結果返回是另一個非同步操作。
註意:p1的狀態就會傳遞給p2,p1的狀態決定了p2的狀態。如果p1的狀態是pending,那麼p2的回調函數就會等待p1的狀態改變;如果p1的狀態已經是 resolved 或者 rejected,那麼p2的回調函數會立即執行。
一般來說,調用resolve 或 reject以後,Promise的進程就就結束了,後續操作應該放到 then方法里,而不是直接寫在 resolve 或 reject 的後面。另外最後在它們之前加上 return語句。
3. Promise.prototype.then()
Promise實例具有 then 方法,then方法是定義在原型對象 Promise.prototype上的。它的作用是為 Promise 實例添加狀態改變時的回調函數。then 的第一個參數是 resolved狀態的回調函數,第二個參數是 rejected狀態的回調函數。
then方法返回的是一個新的 Promise 實例,不是原來那個,因此可以使用鏈式寫法。.then().then()
a().then((j) => {
retrun j
}).then((i) => {
console.log(i)
},(err)=>{
console.log(err)
})
上面 第一個then方法指定的回調函數,返回的是另一個 Promise 對象。這時,第二個 then 方法指定的回調函數,就會等這個新的 Promise對象狀態發生變化,如果變為 resolved,就調用第一個回調函數,如果狀態變為 rejected,就調用第二個回調函數。
4. Promise.prototype.catch()
Promise.prototype.catch 方法是 .then(null, rejecton) 或 .then(undefined, rejection)的別名,用於指定發生錯誤時的回調函數。
a().then((p) => {
// success
}).catch((err) => {
console.log('err')
})
如果對象的狀態變為 resolved, 則會調用 then 方法指定的回調函數 success,如果非同步操作拋出錯誤,狀態就會變為 rejected,就會調用 catch 方法指定的回調函數處理這個錯誤。如果 then 方法指定的回調函數,在運行中拋出錯誤,也會被catch 方法捕獲。
另外reject方法的作用等同於拋出錯誤
如果 Promise狀態已經變成 resolved,再拋出錯誤是無效的。因為狀態一旦改版,就永遠保持,不會再變了。 而且Promise的錯誤有『冒泡』的性質,會一直向後傳遞,直到被捕獲位置,它的錯誤總會被下一個catch語句捕獲。
建議:Promise 對象後面要跟catch方法,這樣可以處理 Promise 內部發生的錯誤。catch方法返回的還是一個 Promise 對象,因此後面還可以接著調用then方法。
註意: catch函數中的方法發生錯誤,如果後面沒有別的catch 方法,那麼錯誤將不會被捕獲,如果 catch 後面 還有catch ,第二個catch將會捕獲前一個catch方法拋出的錯誤。
5. Promise.prototype.finally()
finally 方法用於指定不管 Promise 對象最後狀態如何,都會執行的操作。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
以上,不管promise 最後的狀態,都會執行 finally 方法指定的函數。
finally 方法的回調函數不接受任何參數,所以就無法知道之前Promise狀態到底是 fulfilled 還是 rejected。所以在finally方法裡面的操作,是與之前狀態無關的而且不依賴於Promise的執行結果。
6. Promise.all()
Promise.all 方法用於將多個 Promise 實例,包裝成一個新的 Promise實例。
const p = Promise.all([p1, p2, p3])
Promise.all 方法接受一個數組作為參數,p1、p2、p3都是Promise實例,如果不是,就會先調用Promise.resolve方法,將參數轉為 Promise 實例再處理。(Promise.all 方法的參數可以不是數組,但必須具有 Iterator 介面,且返回的每個成員都是 Promise 實例。)
Promise.all 的狀態有兩種情況:
1、如果 p1 p2 p3的狀態都變成了 fulfilled,p的狀態才是fulfilled,這時候返回一個 p1 p2 p3返回值組成的數組,傳遞給 p 的回調函數。
2、如果 p1 p2 p3中任一一個被rejected,p 的狀態就變成了 rejected,這時候返回的是第一個被 rejected 實例的返回值,傳遞給 p 的回調函數。
註意,如果作為參數的 Promise 實例,自己定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()的catch方法。而是觸發自己定義的catch方法。
7. Promise.race()
Promise.race方法同樣是將多個 Promise 實例,包裝成一個新的 Promise實例。
const p = Promise.race([p1, p2, p3]);
與 Promise.all 的區別就是 p1 p2 p3 中一個實例改變狀態,那麼 p 的狀態就跟著改變了,返回值為最先返回那個Promise實例的返回值。
8. Promise.resolve()
將現有對象轉為 Promise 對象。
1、如果參數是一個Promise 實例
那麼將不做任何修改。
2、參數是一個 thenable對象(具有then方法的對象)
將這個對象轉為Promise對象,然後立即執行 thenable對象的then方法
3、參數不是具有then方法的對象,或根本不是對象
返回一個新的Promise對象,狀態為 resolved
4、不帶任何參數
直接返回一個 resolved 狀態的Promise對象
9. Promise.reject()
Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態為rejected。回調函數立即執行。