1. Promise是什麼
1.1 promise 的理解
1. 抽象表達:
Promise 是 JS 中進行非同步程式設計的新的解決方案(舊的是純回撥形式)
2. 具體表達:
(1)從語法上說:Promise 是一個建構函式
(2)從功能上說:promise 物件用來封裝一個非同步操作並可以獲取其結果
1.2 promise 的狀態和狀態改變
三種狀態:
1. pending: 初始狀態,既不是成功,也不是失敗狀態。
2. fulfilled: 意味著操作成功完成。
3. rejected: 意味著操作失敗。
pending:等待狀態,比如正在進行網路請求,或者定時器沒有到時間。
fulfill:滿足狀態,當我們主動回撥了resolve時,就處於該狀態,並且會回撥.then()
reject:拒絕狀態,當我們主動回撥了reject時,就處於該狀態,並且會回撥.then()或者.catch()
兩種狀態改變:
1. pending 變為 fulfilled
2. pending 變為 rejected
當我們 new Promise 的時候,此時的 Promise物件是 pending 狀態,它可能會變為 fulfilled 狀態並傳遞一個值給相應的狀態處理方法,也可能變為 rejected 狀態並傳遞失敗資訊。當其中任一種情況出現時,Promise 物件的 then 方法繫結的處理方法(handlers )就會被呼叫(then方法包含兩個引數:onfulfilled 和 onrejected,它們都是 Function 型別。當 Promise 狀態為 fulfilled 時,呼叫 then 的 onfulfilled 方法,當 Promise 狀態為 rejected 時,呼叫 then 的 onrejected 方法, 所以在非同步操作的完成和繫結處理方法之間不存在競爭)。
1.3 promise 的基本流程
1.4 promise 的基本使用
//1.建立一個新的promise物件 const p = new Promise((resolve, reject) => { //執行器函式是同步回撥! console.log('執行 executor') //立刻執行 //2.執行非同步操作 setTimeout(() => { const time = Date.now() //3.1 成功,呼叫resolve(value) if( time % 2 === 0 ){ resolve('成功的資料,time=' + time) } else { //3.2 失敗,呼叫reject(reason) reject('失敗的資料,time=' + time) } }, 1000) }) console.log('new Promise()之後') //先輸出 '執行 exceutor' p.then( value => { // onfulfilled函式,自動接收成功的value console.log('成功的回撥', value) }, reason => { // onrejected函式,自動接收失敗的reason console.log('失敗的回撥', reason) } )
2. 為什麼要使用 Promise
(1) 指定回撥方式更加靈活
(2)支援鏈式呼叫, 解決回撥地獄
function successCallback(result) {
console.log('聲音檔案建立成功' + result)
}
function failureCallback(error) { console.log('聲音檔案建立失敗' + error) } /* 1.1 純回撥函式 */ //啟動任務(audioSettings)前必須指定回撥函式(callback) createAudioFileAsync(audioSettings, successCallback, failureCallback) /* 1.2 promise */ //可在啟動任務(audioSettings)後指定回撥函式(callback) const promise = createAudioFileAsync(audioSettings) setTimeout(() => { promise.then(successCallback, failureCallback) }, 1000) /* 2.1 回撥地獄 */ //回撥函式的巢狀 doSomething(function (result) { //第一個函式function就是sucessCallback doSomethingElse(result, function (newResult) { doThirdThing(newResult, function (finalResult) { console.log('Got the final result' + finalResult) }, failureCallback) }, failureCallback) }, failureCallback) /* 2.2 鏈式呼叫 */ doSomething().then(function (result) { //result是doSomething函式成功執行的返回值 return doSomethingElse(result) //執行器函式,同步回撥 }) .then(function (newResult) { //newResult是doSomethingElse成功執行的返回值 return doThirdThing(newResult) }) .then(function (finalResult) { console.log('Got the final result' + finalResult) }) .catch(failureCallback) //統一的錯誤處理 /* 2.3 async/await : 回撥地獄的終極解決方案 */ //相比於promise 去掉了回撥函式 async function request() { try { const result = await doSomething() const newResult = await doSomethingElse(result) const finalResult = await doThirdThing(newResult) console.log('Got the final result' + finalResult) } catch (error) { failureCallback(error) } }
3. 如何使用 Promise
3.1 語法(API)
(1)基本語法
new Promise( function(resolve, reject) {...} /* executor */ );
resolve
和 reject
兩個引數的函式 。Promise建構函式執行時立即呼叫 executor
函式, resolve
和 reject
兩個函式作為引數傳遞給executor
(executor 函式在Promise建構函式返回所建promise例項物件前被呼叫)。resolve
和 reject
函式被呼叫時,分別將promise的狀態改為 fulfilled(完成)或 rejected(失敗)。executor 內部通常會執行一些非同步操作,一旦非同步操作執行完畢(可能成功/失敗),要麼呼叫resolve函式來將promise狀態改成fulfilled,要麼呼叫reject
函式將promise的狀態改為rejected。如果在executor函式中丟擲一個錯誤,那麼該promise 狀態為rejected。executor函式的返回值被忽略。(2)方法
(3)Promise 原型
(4)程式碼展示
new Promise( (resolve, reject) => { setTimeout( () => { resolve('成功') //resolve就像是一個傳遞資料的運輸機 }, 1000 ) }) .then( value => { console.log('onResolved()1', value) } ) .catch( reason => { console.log('onRejected()1', reason) } ) const p1 = new Promise((resolve, reject) => { resolve(1) }) const p2 = Promise.resolve(2) const p3 = Promise.reject(3) // p1.then( value => {console.log(value)} ) // p2.then( value => {console.log(value)} ) // p3.catch( reason => {console.log(reason)} ) //const pAll = Promise.all([p1,p2,p3]) const pAll = Promise.all([p1,p2]) pAll.then( values => { console.log('all onResolved()', values) }, reason => { console.log('all onRejected()', reason) } ) const pRace = Promise.race([p1,p2,p3]) pRace.then( value => { console.log('race onResolved()', value) }, reason => { console.log('race onResolved()', reason) } )
有關更多 promise 的原理和 then,catch,all,race的用法請參考 https://blog.csdn.net/qq_34645412/article/details/81170576、https://segmentfault.com/a/1190000007463101#articleHeader2、https://www.jianshu.com/p/001d22a44f85
3.2 Promise 的幾個關鍵問題
1. 如何改變promise的狀態?
(1) resolve(value): 如果當前是pending就會變為fulfilled
(2) reject(reason): 如果當前是pending就會變為rejected
(3) 丟擲異常: 如果當前是pending就會變為rejected
2. 一個promise指定多個成功/失敗回撥函式, 都會呼叫嗎?
const p = new Promise((resolve, reject) => { //resolve('Promise狀態會被標記為fulfilled') // reject('Promise狀態會被標記為rejected') throw new Error('Promise狀態會被標記為rejected') }); p.then( value => { console.log('value1', value) }, reason => { console.log('reason1', reason) // reason1 Error: Promise狀態會被標記為rejected } )
p.then( value => { console.log('value2', value) }, reason => { console.log('reason2', reason) // reason2 Error: Promise狀態會被標記為rejected } )
3. 改變promise狀態和指定回撥函式誰先誰後?
new Promise((resolve, reject) => { setTimeout(() => { resolve(1) //後改變狀態(同時指定資料),非同步執行回撥函式 }, 1000) }).then( //先指定回撥函式,儲存當前指定的回撥函式 value => {}, reason => { console.log('reason', reason) } ) new Promise((resolve, reject) => { resolve(1) //先改變狀態(同時指定資料) }).then( //後指定回撥函式,非同步執行回撥函式 value => { console.log('value', value) }, reason => { console.log('reason', reason) } ) console.log('-----') //先輸出----, 再輸出value 1,因為回撥函式是非同步執行,會被放入到佇列中待執行
4. promise.then()返回的新promise的結果狀態由什麼決定?
new Promise((resolve, reject) => { // resolve(1) reject(1) }).then( value => { console.log('onResolved1()', value) // return 2 // return Promise.resolve(3) // return Promise.reject(4) throw 5 }, reason => { console.log('onRejected1()', reason) // return 2 // return Promise.resolve(3) // return Promise.reject(4) throw 5 } ).then( value => { console.log('onResolved2()', value) }, reason => { console.log('onRejected2()', reason) } )
5. promise 如何串聯多個操作任務?
new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { // 由於只傳遞了一個引數res 所以可以省略括號 console.log(res, '第一次處理'); // res = res + '111' 這樣比較混亂 return new Promise((resolve) => { // 當我們只需要用resolve的時候可以省略reject resolve(res + '111') // 我們這個時候不需要網路請求 而是自己處理 }).then(res => { console.log(res, '第二次處理'); return new Promise((resolve, reject) => { // resolve(res + '222') reject('error message') }).then(res => { console.log(res, '第三次處理'); }).catch(err => { console.log(err) }) }) })
// 由於第二次和第三次處理都沒有使用非同步處理(setTimeout) 所以我們可以簡寫 return new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { console.log(res, '第一次處理'); return Promise.resolve(res + '111') }).then(res => { console.log(res, '第二次處理'); // return Promise.resolve(res + '222') // return Promise.reject(res + '222') throw 'error message' }).then(res => { console.log(res, '第三次處理'); }).catch(res => { console.log('error message'); })
// 再次簡寫 直接 return res + '111' 省略 Promise.resolve new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { console.log(res, '第一次處理'); return res + '111' }).then(res => { console.log(res, '第二次處理'); return res + '222' }).then(res => { console.log(res, '第三次處理'); })
6. promise異常傳透?
new Promise((resolve, reject) => { //resolve(1) reject(1) }).then( value => { console.log('onResolved1()', value) return 2 }, // 內部預設會將錯誤逐級傳遞直至最後的catch // reason => Promise.reject(reason) // reason => { // throw reason // } ).then( value => { console.log('onResolved2()', value) return 3 } ).then( value => { console.log('onResolved3()', value) } ).catch(reason => { console.log('onRejected1()', reason) })
7. 中斷promise鏈?
new Promise((resolve, reject) => { reject(1) }).then( value => { console.log('onResolved1()', value) return 2 } ).then( value => { console.log('onResolved2()', value) return 3 } ).then( value => { console.log('onResolved3()', value) } ).catch(reason => { console.log('onRejected1()', reason) return new Promise(() => {}) //返回一個pending的promise 中斷promise鏈,後面程式碼不會再執行 }).then( value => { console.log('onResolved4()', value) }, reason => { console.log('onRejected4()', reason) } )