做電影網(wǎng)站用什么空間怎樣做好網(wǎng)絡營銷推廣
目錄
什么是 Promise
實現(xiàn)一個 Promise
Promise 的聲明
解決基本狀態(tài)
添加 then 方法
解決異步實現(xiàn)
解決鏈式調(diào)用
完成 resolvePromise 函數(shù)
解決其他問題
添加 catch 方法
添加 finally 方法
添加 resolve、reject、race、all 等方法
如何驗證我們的 Promise 是否正確?
什么是 Promise
Promise 是一種異步編程的解決方案。在異步操作中,callback 會導致回調(diào)地獄的問題,Promise 解決了這個問題。
一個 Promise對象有以下三種狀態(tài):
- pending:初始狀態(tài),既不是成功,也不是失敗狀態(tài)。
- fulfilled(resolved):意味著操作成功完成。
- rejected:意味著操作失敗。
Promise對象內(nèi)部運行的一個變化, 變化如下:
- 當 new Promise() 被實例化后,即表示 Promise 進入 pending 初始化狀態(tài),準備就緒,等待運行。
- 一旦 Promise 實例運行成功或者失敗之后,實例狀態(tài)就會變?yōu)?fulfilled 或者 rejected,此時狀態(tài)就無法變更。
在使用 Promise 時,通常會調(diào)用其 then() 方法來處理異步操作的結果,或者調(diào)用 catch() 方法來處理出錯信息。同時,Promise 還提供了一些靜態(tài)方法,如 Promise.resolve()、Promise.reject() 等用于快速創(chuàng)建一個 Promise 實例。
實現(xiàn)一個 Promise
(下文所指的規(guī)定是指?Promise A+規(guī)范)
Promise 的聲明
首先呢,promise 肯定是一個類,我們就用 class 來聲明。
- 由于 new Promise((resolve, reject)=>{}) ,所以傳入一個參數(shù)(函數(shù) executor),傳入就執(zhí)行
- executor 里面有兩個參數(shù),一個叫 resolve(成功),一個叫 reject(失敗)
- 由于 resolve 和 reject 可執(zhí)行,所以都是函數(shù),我們用 let 聲明
class myPromise{// 構造器constructor(executor){// 成功let resolve = () => { };// 失敗let reject = () => { };// 立即執(zhí)行executor(resolve, reject);}
}
解決基本狀態(tài)
Promise 有規(guī)定:
- Promise 存在三個狀態(tài)(state)pending、fulfilled、rejected
- pending(等待態(tài))為初始態(tài),并可以轉化為 fulfilled(成功態(tài))和 rejected(失敗態(tài))
- 成功時,不可轉為其他狀態(tài),且必須有一個不可改變的值(value)
- 失敗時,不可轉為其他狀態(tài),且必須有一個不可改變的原因(reason)
- new Promise((resolve, reject)=>{resolve(value)}) resolve 為成功,接收參數(shù) value,狀態(tài)改變?yōu)?fulfilled,不可再次改變。
- new Promise((resolve, reject)=>{reject(reason)}) reject 為失敗,接收參數(shù) reason,狀態(tài)改變?yōu)?rejected,不可再次改變。
- 若是 executor 函數(shù)報錯 直接執(zhí)行 reject();
于是乎,我們獲得以下代碼
class myPromise {constructor(executor) {// 初始化 state 為等待態(tài)this.state = 'pending'// 成功的值this.value = undefined// 失敗的原因this.reason = undefinedlet resolve = value => {// state 改變 resolve 調(diào)用就會失敗if (this.state === 'pending') {// resolve 調(diào)用后 state 轉化為成功態(tài)this.state = 'fulfilled'}// 儲存成功的值this.value = value}let reject = reason => {// state 改變 reject 調(diào)用就會失敗if (this.state === 'pending') {// reject 調(diào)用后 state 轉化為失敗態(tài)this.state = 'rejected'}// 儲存失敗的原因this.reason = reason}// 立即執(zhí)行 executor,如果 executor 執(zhí)行報錯,直接執(zhí)行 rejecttry {executor(resolve, reject)} catch(err) {reject(err)}}
}
添加 then 方法
Promise 有一個叫做 then 的方法,里面有兩個參數(shù):onFulfilled 和 onRejected,成功有成功的值,失敗有失敗的原因
- 當狀態(tài) state 為 fulfilled,則執(zhí)行 onFulfilled,傳入 this.value。
- 當狀態(tài) state 為 rejected,則執(zhí)行 onRejected,傳入 this.reason
- onFulfilled 和 onRejected 如果是函數(shù),則必須分別在 fulfilled,rejected 后被調(diào)用,value 或reason 依次作為他們的第一個參數(shù)
class myPromise {constructor(executor){...}// then 方法有兩個參數(shù) onFulfilled 和 onRejectedthen(onFulfilled, onRejected) {// 狀態(tài)為 fulfilled,執(zhí)行 onFulfilled,傳入成功的值if (this.state === 'fulfilled') {onFulfilled(this.value)}// 狀態(tài)為 rejected,執(zhí)行 onRejected,傳入失敗的原因if (this.state === 'rejected') {onRejected(this.reason)}}
}
解決異步實現(xiàn)
現(xiàn)在基本可以實現(xiàn)簡單的同步代碼,但是當 resolve 在 setTomeout 內(nèi)執(zhí)行,調(diào)用 then 時 state 還是 pending 等待態(tài),我們就需要在 then 調(diào)用的時候,將成功和失敗存到各自的數(shù)組,一旦 reject 或者 resolve,就調(diào)用它們
類似于發(fā)布訂閱,先將 then 里面的兩個函數(shù)儲存起來,由于一個 promise 可以有多個 then,所以存在同一個數(shù)組內(nèi)。
// 多個 then 的情況
let p = new Promise();
p.then();
p.then();
成功或者失敗時,forEach 調(diào)用它們
class myPromise {constructor(executor) {this.state = 'pending'this.value = undefinedthis.reason = undefined// 成功存放的數(shù)組this.onResolvedCallbacks = []// 失敗存放的數(shù)組this.onRejectedCallbacks = []let resolve = value => {if (this.state === 'pending') {this.state = 'fulfilled'}this.value = value// 一旦 resolve 執(zhí)行,調(diào)用成功數(shù)組的函數(shù)this.onResolvedCallbacks.forEach(fn => fn());}let reject = reason => {if (this.state === 'pending') {this.state = 'rejected'}this.reason = reason// 一旦 reject 執(zhí)行,調(diào)用失敗數(shù)組的函數(shù)this.onRejectedCallbacks.forEach(fn => fn())}try {executor(resolve, reject)} catch(err) {reject(err)}}then(onFulfilled, onRejected) {if (this.state === 'fulfilled') {onFulfilled(this.value)}if (this.state === 'rejected') {onRejected(this.reason)}// 狀態(tài)為 pending 時if (this.state === 'pending') {// onFulfilled 傳入到成功數(shù)組this.onResolvedCallbacks.push(() => onFulfilled(this.value))// onRejected 傳入到失敗數(shù)組this.onRejectedCallbacks.push(() => onRejected(this.reason))}}
}
解決鏈式調(diào)用
我門常常用到 new Promise().then().then(),這就是鏈式調(diào)用,用來解決回調(diào)地獄
- 為了達成鏈式,我們默認在第一個 then 里返回一個 promise。規(guī)定了一種方法,就是在then 里面返回一個新的 promise 稱為 promise2
let promise2 = new Promise((resolve, reject)=>{}) 1. 將這個 promise2 返回的值傳遞到下一個 then 中 2. 如果返回一個普通的值,則將普通的值傳遞給下一個 then 中
- 當我們在第一個 then 中 return 了一個參數(shù)(參數(shù)未知,需判斷)。這個 return 出來的新的promise 就是 onFulfilled() 或 onRejected() 的值
規(guī)定 onFulfilled() 或 onRejected() 的值,即第一個 then 返回的值,叫做 x,判斷 x 的函數(shù)叫做 resolvePromise
class myPromise {constructor(executor){...}then(onFulfilled, onRejected) { // 聲明返回的 promise2let promise2 = new myPromise((resolve, reject) => {if (this.state === 'fulfilled') {let x = onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)}if (this.state === 'rejected') {let x = onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)}if (this.state === 'pending') {this.onResolvedCallbacks.push(() => {let x = onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)})this.onRejectedCallbacks.push(() => {let x = onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)})}})// 返回 Promise,完成鏈式return promise2}
}
完成 resolvePromise 函數(shù)
規(guī)定了一段代碼,讓不同的 promise 代碼互相套用,叫做 resolvePromise
- 如果 x === promise2,會造成循環(huán)引用,自己等待自己完成,則報“循環(huán)引用”錯誤
let p = new Promise(resolve => {resolve(0); }); var p2 = p.then(data => {// 循環(huán)引用,自己等待自己完成,一輩子完不成return p2; })
- x 不能是 null
- x 是普通值,直接 resolve(x)
- x 是對象或者函數(shù)(默認 promise),?let then = x.then
- 如果取 then 報錯,則走 reject()
- 如果 then 是個函數(shù),則用 call 執(zhí)行 then,第一個參數(shù)是 x,后面是成功的回調(diào)和失敗的回調(diào)
- 如果成功的回調(diào)還是 pormise,就遞歸繼續(xù)解析
- 因為成功和失敗只能調(diào)用一個,所以設定一個 called 來防止多次調(diào)用
// 處理 x 的值,如果是普通值直接返回,如果是 promise 則返回 x.then 執(zhí)行的結果
function resolvePromise(promise2, x, resolve, reject) {// 如果 new 出來的 Promise2 和 x 是同一個,循環(huán)引用報錯if (promise2 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));// 先判斷是不是對象或者函數(shù)if (x !== null && (typeof x === 'object' || typeof x === 'function')) {// 調(diào)用了成功就不能失敗,調(diào)用了失敗就不能成功,不能多次調(diào)用成功或者失敗let called// 內(nèi)部可能拋出錯誤try {// 聲明 then = x 的 then 方法let then = x.then// 如果 then 是函數(shù),就默認是 promise 了if (typeof then === 'function') {this.call(x, res => {// 成功和失敗只能調(diào)用一個if (called) returncalled = true// res 可能是一個 promise,遞歸調(diào)用 resolvePromise,直到解析出的值是普通值resolvePromise(promise2, res, resolve, reject)}, err => {// 成功和失敗只能調(diào)用一個if (called) returncalled = true// 直接調(diào)用 reject 作為失敗的結果,并向下傳遞reject(err)})} else {// 如果 then 不是函數(shù),就說明是個普通值,直接返回 xresolve(x)}} catch(err) {// 也屬于失敗,成功和失敗只能調(diào)用一個if (called) returncalled = true// 直接調(diào)用 reject 作為失敗的結果,并向下傳遞reject(err)}} else {// x 是普通值,直接返回resolve(x)}
}
解決其他問題
- 規(guī)定 onFulfilled 和 onRejected 都是可選參數(shù),如果他們不是函數(shù),必須被忽略
- onFulfilled 返回一個普通的值,成功時直接等于 value => value
- onRejected 返回一個普通的值,失敗時如果直接等于 value => value,則會跑到下一個 then 中的 onFulfilled 中,所以直接扔出一個錯誤 reason => throw err
- 規(guī)定 onFulfilled 或 onRejected 不能同步被調(diào)用,必須異步調(diào)用。我們就用 setTimeout 解決異步問題
- 如果 onFulfilled 或 onRejected 報錯,則直接返回 reject()
class myPromise {constructor(executor) {this.state = 'pending'this.value = undefinedthis.reason = undefinedthis.onResolvedCallbacks = []this.onRejectedCallbacks = []let resolve = value => {if (this.state === 'pending') {this.state = 'fulfilled'}this.value = valuethis.onResolvedCallbacks.forEach(fn => fn());}let reject = reason => {if (this.state === 'pending') {this.state = 'rejected'}this.reason = reasonthis.onRejectedCallbacks.forEach(fn => fn())}try {executor(resolve, reject)} catch(err) {reject(err)}}then(onFulfilled, onRejected) {// onFulfilled 如果不是函數(shù),忽略 onFulfilled, 直接返回 valueonFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val// onRejected 如果不是函數(shù),忽略 onRejected, 讓它等于一個函數(shù),并且在函數(shù)內(nèi)繼續(xù)將 err 向下拋出onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}let promise2 = new Promise((resolve, reject) => {if (this.state === 'fulfilled') {// 異步setTimeout(() => {try {let x = onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)} catch(err) {reject(err)}}, 0)}if (this.state === 'rejected') {// 異步setTimeout(() => {try {let x = onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)} catch(err) {reject(err)}}, 0)}if (this.state === 'pending') {this.onResolvedCallbacks.push(() => {// 異步setTimeout(() => {try {let x = onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)} catch(err) {reject(err)}}, 0)})this.onRejectedCallbacks.push(() => {// 異步setTimeout(() => {try {let x = onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)} catch(err) {reject(err)}}, 0)})}})return promise2}
}
添加 catch 方法
class myPromise {constructor(executor) {...}then(onFulfilled, onRejected) {...}// Promise 中的 catch 指代的就是 then 沒有成功回調(diào)的一個別名而已catch(errCallback) {return this.then(null, errCallback);}
}
添加 finally 方法
// 無論如何都會執(zhí)行,把上一個 then 的結果向下傳遞
// 如果 finally 中返回了一個 Promise 會等待這個 Promise 執(zhí)行完成后繼續(xù)執(zhí)行
myPromise.prototype.finally = function(callback) {return this.then(val => {return Promise.resolve(callback()).then(() => val);}, (err) => {return Promise.resolve(callback()).then(() => { throw err; });});
};
添加 resolve、reject、race、all 等方法
// Promise.resolve 會等待里面的 Promise 執(zhí)行成功
Promise.resolve = val => {return new Promise((resolve) => {resolve(val);});
};// Promise.reject 不會等待參數(shù)中的 Promise 執(zhí)行完畢
Promise.reject = () => {return new Promise((_, reject) => {reject(val);});
};// all 方法表示等待所有的 Promise 全部成功后才會執(zhí)行回調(diào)
// 如果有一個 Promise 失敗則 Promise 就失敗了
Promise.all = promises => {return new Promise((resolve, reject) => {// 存放結果const res = [];// 計數(shù),當count 等于 length的時候就resolvelet count = 0;const resolveRes = (index, data) => {// 將執(zhí)行結果緩存在res中res[index] = data;// 所有子項執(zhí)行完畢之后,執(zhí)行resolve 拋出所有的執(zhí)行結果if (++count === promises.length) {resolve(res);}};// 循環(huán)遍歷每一個參數(shù)的每一項for(let i = 0; i < promises.length; i++) {const current = promises[i];// 如果當前項是Promise,則返回 then 的結果if (isPromise(current)) {current.then((data) => {resolveRes(i, data);}, (err) => {reject(err);});} else {resolveRes(i, current);}}});
}// race誰是第一個完成的,就用他的結果
// 如果失敗這個 Promise 就失敗,如果第一個是成功就是成功
Promise.race = (promises) => {return new Promise((resolve, reject) => {for(let i = 0; i < promises.length; i++) {let current = promises[i];// 如果是一個 promise 繼續(xù)執(zhí)行 thenif (isPromise(current)) {current.then(resolve, reject);} else {// 是普通值則直接 resolve 返回,并終止循環(huán)resolve(current);break;}}});
}
如何驗證我們的 Promise 是否正確?
- 先在后面加上下述代碼
// 目前是通過他測試 他會測試一個對象 // 語法糖 Promise.defer = Promise.deferred = function () {let dfd = {}dfd.promise = new Promise((resolve,reject)=>{dfd.resolve = resolve;dfd.reject = reject;});return dfd; } module.exports = Promise; //npm install promises-aplus-tests 用來測試自己的 promise 符不符合 promises A+ 規(guī)范
- npm 有一個 promises-aplus-tests 插件
// Windows 全局安裝 npm i promises-aplus-tests -g // Mac 全局安裝 sudo npm i promises-aplus-tests -g
- 命令行 promises-aplus-tests [js文件名],即可驗證
注意??:本文 Promise 已滿足基本使用,但還是存在一些問題待改進。。。
參考
史上最最最詳細的手寫Promise教程
Promise 實現(xiàn)原理