經過手寫符合A+規範的promise,來深刻了解Promise,再結合相關面試題,爭取作到在面試的時候,若是問Promise,我們能全方位吊打面試官😁😁😁
下面的每個寫法都對應Promise的一些特性,不斷升級,瞭解原理後再作題就會發現很簡單了es6
詳細介紹的話你們去看 阮一峯es6-promise,我這裏當你已經有必定的基礎了,而後咱們總結一下基本特性面試
new Promise((resolve,reject)=>{ //excutor setTiemout(()=>{ resolve(1) //resolve中的值會傳遞到成功的回調函數參數中 },1000) }).then((val)=>{ //onFulfiled console.log(val) },(e)=>{ //onRejected console.log(e) })
pending
思路:一、二、三、5都比較好實現,4的話採用發佈訂閱模式也能實現,發佈訂閱模式可看我這篇文章發佈訂閱模式npm
class Promise { constructor(executor) { this.status='pending' //三狀態 this.value = undefined //參數 this.reason = undefined this.onFulfilled = [] //發佈訂閱中儲存回調 this.onRejected = [] let resolve = (value)=>{ if(this.status==='pending'){ this.status = 'fulfilled' this.value = value this.onFulfilled.forEach(fn=>fn(this.value)) //發佈訂閱模式,異步一改變狀態則當即執行回調 } } let reject = (reason)=>{ if(this.status==='pending'){ this.status = 'rejected' this.reason = reason this.onRejected.forEach(fn=>fn(this.reason)) } } try{ executor(resolve,reject) //executor同步執行 }catch (e) { reject(e) } } then(onFulfilled, onRejected) { // 若是then的時候 根據當前狀態執行成功或者失敗 if(this.status==='fulfilled'){ onFulfilled(this.value) } if(this.status==='rejected'){ onRejected(this.reason) } if(this.status==='pending'){ this.onFulfilled.push(onFulfilled) //發佈訂閱模式儲存異步回調 this.onRejected.push(onRejected) } } }
1.若是promise中的then方法,不管是成功仍是失敗,他的返回結果是一個普通的時候就會把這個結果傳遞給外層的then的下一個then的成功回調segmentfault
Promise.reject().then((val)=>{ return 'ok' },()=>{ return 'err' }).then((val)=>{ console.log('ok' + val) },(e)=>{ console.log('err' + e) }) // okerr 第一個then失敗的回調返回的是普通值,仍是走第二個的then中成功回調
2.若是成功或者失敗的回調的返回值 返回是一個promise 那麼會讓這個promise執行 採用他的狀態數組
Promise.resolve().then(()=>{ return new Promise((resolve)=>{ setTimeout(()=>{ resolve(1) },1000) }) }).then((val)=>{ console.log(val) }) //一秒後打印1
這一版主要是實現鏈式調用,稍微繞一點,可是理清楚了也不難
首先明確一下,then後面會返回一個新的Promise,因此才能執行鏈式調用
第一個比較繞的地方,怎麼讓第二個then裏面的回調執行?只要調用建立promise2時的resolve方法就好了
第二個標膠繞的地方就是參數是什麼?咱們看特性3.1,參數是什麼要根據第一個then中回調的返回值來判斷,返回值若是是正常值,若是是Piomise,若是報錯處理都不同,因此咱們封裝一個resolvePromise的方法來處理
promise
須要改變的核心代碼以下 let resolvePromise = (promise2, x, resolve, reject) => {...} class Promise { construcotr(){...} then(){ let promise2 = new promise((resolve,reject)=>{ let x = onFulfiled() // onFulfilef是第一個then中的回調函數 resolvePromise(promise2,x, resolve, reject) }) return promiese2 } }
resolvePromise這個方法會判斷onFulfiled返回值類型,若是是普通值會怎麼樣,若是是一個Promise會怎麼樣,若是報錯會怎麼樣,詳細實現方法能夠參考promise A+規範
完整實現異步
let resolvePromise = (promise2, x, resolve, reject) => { // 監測到環形鏈 if(promise2===x) return new TypeError('chaining cycle detected for promise') if(typeof x ==='function' ||(typeof x ==='object' && x!==null)){ try{ //嘗試取出then,有問題報錯 let then = x.then if(typeof then === 'function'){ //這裏是最繞的,想清楚promise2和x的關係,x.then會不會執行取決於使用者的邏輯,會不會在第一個then中回調函數中返回的promise中調用它的resolve改變狀態 then.call(x,resolve,reject) }else{// then不是function resolve(x) } }catch (e) { reject(e) } }else{ //普通類型 resolve(x) } } class Promise { constructor(executor) { this.status = 'pending' this.value = undefined this.reason = undefined this.onFulfilledCallback = [] this.onRejectedCallback = [] let resolve = (value) => { if (this.status === 'pending') { this.status = 'fulfilled' this.value = value this.onFulfilledCallback.forEach(fn => fn(this.value)) } } let reject = (reason) => { if (this.status === 'pending') { this.status = 'rejected' this.reason = reason this.onRejectedCallback.forEach(fn => fn(this.reason)) } } try { executor(resolve, reject) } catch (e) { reject(e) } } then(onFulfilled, onRejected) { // 若是then的時候 根據當前狀態執行成功或者失敗 let promise2 = new Promise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { //這裏之因此異步是由於必須保證resolvePromise(promise2, x, resolve, reject)時Promise2建立完成 try { let x = onFulfilled(this.value) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) } if (this.status === 'rejected') { setTimeout(() => { try { let x = onRejected(this.reason) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) } if (this.status === 'pending') { this.onFulfilledCallback.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) this.onRejectedCallback.push(() => { setTimeout(() => { try { let x = onRejected(this.reason) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) } }) return promise2 } }
基本面試5-10分鐘代碼寫到這裏,都能給滿分經過,剩下的就是4個打補丁的地方了函數
其實是A+規範測試用例的補丁,我按重要程度往下排,前面的必須作到能寫出來(面試能夠不寫),後面的知道便可測試
Promise.resolve(1).then().then().then().then((val)=>{ console.log(val) //1 }) //失敗也是相似的傳遞
能夠默認傳遞一個回調函數
then(onFufilled,onRejected){this
onFufilled = typeof onFufilled === 'function'?onFufilled:value=>value; ...
}
這個也不難,遞歸調用resolvePromise去解析
let resolvePromise = (promise2,x,resolve,reject) => { ... then = x.then /*這個是以前的核心代碼 then.call(x,resolve,reject) *實際等同於 then.call(x,(y)=>{ * resolve(y) 這個y是x做爲promise的返回值,如今這個y多是個promise因此再遞歸調用resolvePromise去解析 * },reject) */ 改爲這樣: then.call(x,(y)=>{ resolvePromise((promise2,y,resolve,reject) },reject) ... }
constructor(executor){ ... let resolve = (value) =>{ // 若是resolve的值時一個promise if(value instanceof Promise){ // 我就讓這個promise執行,把成功的結果再次判斷 return value.then(resolve,reject) //參數會自動傳遞到resolve裏面去 } } ...
Promise比較重要的方法一共有五個方法
把一個對象包裝成Promise對象,特別注意狀態不必定是成功的
各類注意事項請看阮一峯es6-promise
直接記憶很差記憶,可是結合源碼很簡單,理所固然
static resolve(value){ return new Promise((resolve,reject)=>{ resolve(value); }) }
Promise.reject(reason)
方法也會返回一個新的 Promise 實例,該實例的狀態爲rejected
。
static reject(err){ return new Promise((resolve,reject)=>{ reject(err); }) }
這個是實例方法,其餘幾個都是類方法
不管成功仍是失敗都會調用,因此可定返回的也是一個Promimse,成功失敗都會調用傳入的回調,
finally不接受值,返回的Promise的狀態受前一個promise狀態的影響
finally若是在中間同時回調返回一個promise則會等待promise
Promise.resolve(1).finally( (a)=>{ return new Promise((resolve)=>{ setTimeout(function () { resolve(2) },3000) }) } ).then((data)=>{ console.log(data) }) 等待3秒後打印1
finally實現
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
race和all一個是誰先調用誰執行後面then中的回調,一個是所有調用才執行後面then中的回調
他們都須要對參數中傳入的數組進行遍歷
all的實現須要藉助計數器,這也是實現異步任務通知的一種方法
直接完成或者異步完成都會使計數器加1 當計數器和數組長度相等時就是all方法完成的時候
,而後把結果數組傳到下一個回調
race的實現就是,遍歷數組中元素current,都去改變返回promise的值,誰先改變就取誰的值傳到會帶哦函數裏面
return promose((resolve,reject)=>{ if(isPromise(current)){ current.then(resolve,reject) }else{ resolve(current) } })
具體實現見6
let resolvePromise = (promise2,x,resolve,reject) => { if(promise2 === x){ return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } // 若是調用了失敗 就不能再調用成功 調用成功也不能再調用失敗 let called; if(typeof x ==='function' || (typeof x === 'object' && x!== null) ){ try{ let then = x.then; // Object,dedefineProperty if(typeof then === 'function'){ then.call(x,(y)=>{ // x.then(y=>,err=>) if(called) return; called = true // y有可能解析出來的仍是一個promise // 在去調用resolvePromise方法 遞歸解析的過程 // resolve(y) resolvePromise(promise2,y,resolve,reject); // 總有y是普通值的時候 },e=>{ if(called) return; called = true reject(e); }) }else{ if(called) return; called = true resolve(x); } }catch(e){ if(called) return; called = true reject(e); } }else{ if(called) return; called = true resolve(x); // '123' 123 } } class Promise{ constructor(executor){ this.value = undefined; this.reason = undefined; this.status = 'pending'; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = (value) =>{ // 若是resolve的值時一個promise // if(typeof value === 'function' || (typeof value == 'object'&&value !== null)){ // if(typeof value.then == 'function'){ // return value.then(resolve,reject) // } // } if(value instanceof Promise){ // 我就讓這個promise執行,把成功的結果再次判斷 return value.then(resolve,reject) //參數會自動傳遞到resolve裏面去 } if(this.status === 'pending'){ this.status = 'fulfilled' this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } } let reject = (reason) =>{ if(this.status === 'pending'){ this.status = 'rejected' this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } } try{ executor(resolve,reject); }catch(e){ console.log(e) reject(e); } } then(onFufilled,onRejected){ // 可選參數的配置 onFufilled = typeof onFufilled === 'function'?onFufilled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err} let promise2 = new Promise((resolve,reject)=>{ if(this.status === 'fulfilled'){ setTimeout(()=>{ // 爲了保證promise2 已經產生了 try{ let x = onFufilled(this.value); resolvePromise(promise2,x,resolve,reject); }catch(e){ console.log(e); reject(e); } }) } if(this.status === 'rejected'){ setTimeout(() => { try{ let x= onRejected(this.reason); resolvePromise(promise2,x,resolve,reject); }catch(e){ reject(e); } }); } if(this.status === 'pending'){ this.onResolvedCallbacks.push(()=>{ setTimeout(() => { try{ let x = onFufilled(this.value); resolvePromise(promise2,x,resolve,reject); }catch(e){ reject(e); } }) }); this.onRejectedCallbacks.push(()=>{ setTimeout(() => { try{ let x= onRejected(this.reason); resolvePromise(promise2,x,resolve,reject); }catch(e){ reject(e); } }); }); } }) return promise2 } finally(callback){ let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); } catch(errCallback){ // catch是then的一個別名而已 return this.then(null,errCallback) } static resolve(value){ return new Promise((resolve,reject)=>{ resolve(value); }) } static reject(err){ return new Promise((resolve,reject)=>{ reject(err); }) } static race(values){ return new Promise((resolve,reject)=>{ for(let i = 0 ; i<values.length;i++){ let current = values[i]; if(isPromise(current)){ current.then(resolve,reject) }else{ resolve(current) } } }) } static all(values){ return new Promise((resolve,reject)=>{ let arr = []; // 最終的結果 let i = 0; function processData(key,val) { arr[key] = val; if(++i == values.length){ resolve(arr); } } for(let i = 0 ; i<values.length;i++){ let current = values[i]; if(isPromise(current)){ current.then(y=>{ processData(i,y); },reject) }else{ processData(i,current); } } }) } } Promise.deferred = () => { // 測試方法 let dfd = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }) return dfd; // 能夠檢測這個對象上的promise屬性 resolve方法 reject方法 } module.exports = Promise; // 全局安裝 只能在命令中使用 sudo npm install promises-aplus-tests -g // promises-aplus-tests promise.js // 本地安裝 能夠在命令下 和 咱們的代碼中使用
Promise.reject(1).then().finally( (a)=>{ console.log('a:'a) //undefined setTimeout(function () { console.log(2) },3000) } ).then((data)=>{ console.log(3) console.log(data) },(e)=>{ console.log('error'+e) //打印error1 }) //
答案:a:undefined error1 過兩秒 2
來源:微醫
答案:同步,異步 源碼裏面寫的很清楚
答案:
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
使用:須要同時獲取多個東西后再執行回調
原理:返回一個Promise: p 遍歷參數數組,若不是promise,直接加入到結果數組arr中 計數器++
若是是Promise,等Promise執行完再講結果加到加過數組 計數器++
計數器===數組長度時證實所有完成,p.resolve(結果數組arr)
錯誤處理: p.reject(e)
答案:
Promise._race = promises => new Promise((resolve, reject) => { promises.forEach(promise => { promise.then(resolve, reject) }) })
總結了Promise的實現,以及面試常見考點,相信若是所有理解了,面試再問promise確定能夠加分很多。因爲技術有限,若是閱讀中發現有什麼錯誤,請在留言指出