咱們工做中免不了運用promise用來解決異步回調問題。平時用的不少庫或者插件都運用了promise 例如axios、fetch等等。可是你知道promise是咋寫出來的呢?javascript
別怕~這裏有本promisesA+規範,便宜點10元賣給你了。java
一、Promise 的聲明ios
首先呢,promise確定是一個類,咱們就用class來聲明。
• 因爲 new Promise((resolve, reject)=>{}) ,因此傳入一個參數(函數),祕籍裏叫他executor,傳入就執行。
•executor裏面有兩個參數,一個叫resolve(成功),一個叫reject(失敗)。
•因爲resolve和reject可執行,因此都是函數,咱們用let聲明。npm
class Promise{ // 構造器 constructor(executor){ // 成功 let resolve = () => { }; // 失敗 let reject = () => { }; // 當即執行 executor(resolve, reject); } }
解決基本狀態axios
祕籍對Promise有規定:數組
•Promise存在三個狀態(state)pending、fulfilled、rejectedpromise
•pending(等待態)爲初始態,並能夠轉化爲fulfilled(成功態)和rejected(失敗態)bash
•成功時,不可轉爲其餘狀態,且必須有一個不可改變的值(value)異步
•失敗時,不可轉爲其餘狀態,且必須有一個不可改變的緣由(reason)函數
• new Promise((resolve, reject)=>{resolve(value)}) resolve爲成功,接收參數value,狀態改變爲fulfilled,不可再次改變。
• new Promise((resolve, reject)=>{reject(reason)}) reject爲失敗,接收參數reason,狀態改變爲rejected,不可再次改變。
•如果executor函數報錯 直接執行reject();
因而乎,咱們得到如下代碼:
class Promise{ constructor(executor){ // 初始化state爲等待態 this.state = 'pending'; // 成功的值 this.value = undefined; // 失敗的緣由 this.reason = undefined; let resolve = value => { // state改變,resolve調用就會失敗 if (this.state === 'pending') { // resolve調用後,state轉化爲成功態 this.state = 'fulfilled'; // 儲存成功的值 this.value = value; } }; let reject = reason => { // state改變,reject調用就會失敗 if (this.state === 'pending') { // reject調用後,state轉化爲失敗態 this.state = 'rejected'; // 儲存失敗的緣由 this.reason = reason; } }; // 若是executor執行報錯,直接執行reject try{ executor(resolve, reject); } catch (err) { reject(err); } } }
then方法
祕籍規定:Promise有一個叫作then的方法,裏面有兩個參數:onFulfilled,onRejected,成功有成功的值,失敗有失敗的緣由
•當狀態state爲fulfilled,則執行onFulfilled,傳入this.value。當狀態state爲rejected,則執行onRejected,傳入this.value
•onFulfilled,onRejected若是他們是函數,則必須分別在fulfilled,rejected後被調用,value或reason依次做爲他們的第一個參數
class Promise{ constructor(executor){...} // then 方法 有兩個參數onFulfilled onRejected then(onFulfilled,onRejected) { // 狀態爲fulfilled,執行onFulfilled,傳入成功的值 if (this.state === 'fulfilled') { onFulfilled(this.value); }; // 狀態爲rejected,執行onRejected,傳入失敗的緣由 if (this.state === 'rejected') { onRejected(this.reason); }; } }
這下武學初成,能夠對付對付江湖小雜毛了,可是對於帶setTimeout的江洋大盜仍是沒轍。
解決異步實現
如今基本能夠實現簡單的同步代碼,可是當resolve在setTomeout內執行,then時state仍是pending等待狀態 咱們就須要在then調用的時候,將成功和失敗存到各自的數組,一旦reject或者resolve,就調用它們
相似於發佈訂閱,先將then裏面的兩個函數儲存起來,因爲一個promise能夠有多個then,因此存在同一個數組內。
// 多個then的狀況 let p = new Promise(); p.then(); p.then();
成功或者失敗時,forEach調用它們
class Promise{ constructor(executor){ this.state = 'pending'; this.value = undefined; this.reason = undefined; // 成功存放的數組 this.onResolvedCallbacks = []; // 失敗存放法數組 this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; // 一旦resolve執行,調用成功數組的函數 this.onResolvedCallbacks.forEach(fn=>fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; // 一旦reject執行,調用失敗數組的函數 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); }; // 當狀態state爲pending時 if (this.state === 'pending') { // onFulfilled傳入到成功數組 this.onResolvedCallbacks.push(()=>{ onFulfilled(this.value); }) // onRejected傳入到失敗數組 this.onRejectedCallbacks.push(()=>{ onRejected(this.value); }) } } }
解決鏈式調用
我門經常用到 new Promise().then().then() ,這就是鏈式調用,用來解決回調地獄
一、爲了達成鏈式,咱們默認在第一個then裏返回一個promise。祕籍規定了一種方法,就是在then裏面返回一個新的promise,稱爲promise2: promise2 = new Promise((resolve, reject)=>{})
•將這個promise2返回的值傳遞到下一個then中
•若是返回一個普通的值,則將普通的值傳遞給下一個then中
二、當咱們在第一個then中 return 了一個參數(參數未知,需判斷)。這個return出來的新的promise就是onFulfilled()或onRejected()的值
祕籍則規定onFulfilled()或onRejected()的值,即第一個then返回的值,叫作x,判斷x的函數叫作resolvePromise
return
class Promise{ constructor(executor){ this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } }; try{ executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled,onRejected) { // 聲明返回的promise2 let promise2 = new Promise((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.value); resolvePromise(promise2, x, resolve, reject); }) } }); // 返回promise,完成鏈式 return promise2; } }
完成resolvePromise函數
祕籍規定了一段代碼,讓不一樣的promise代碼互相套用,叫作resolvePromise
•若是 x === promise2,則是會形成循環引用,本身等待本身完成,則報「循環引用」錯誤
let p = new Promise(resolve => { resolve(0); }); var p2 = p.then(data => { // 循環引用,本身等待本身完成,一生完不成 return p2; })
一、判斷x
• Otherwise, if x is an object or function,Let then be x.then
•x 不能是null
•x 是普通值 直接resolve(x)
• x 是對象或者函數(包括promise), let then = x.then 二、當x是對象或者函數(默認promise)
•聲明瞭then
•若是取then報錯,則走reject()
•若是then是個函數,則用call執行then,第一個參數是this,後面是成功的回調和失敗的回調
•若是成功的回調仍是pormise,就遞歸繼續解析 三、成功和失敗只能調用一個 因此設定一個called來防止屢次調用
function resolvePromise(promise2, x, resolve, reject){ // 循環引用報錯 if(x === promise2){ // reject報錯 return reject(new TypeError('Chaining cycle detected for promise')); } // 防止屢次調用 let called; // x不是null 且x是對象或者函數 if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { // A+規定,聲明then = x的then方法 let then = x.then; // 若是then是函數,就默認是promise了 if (typeof then === 'function') { // 就讓then執行 第一個參數是this 後面是成功的回調 和 失敗的回調 then.call(x, y => { // 成功和失敗只能調用一個 if (called) return; called = true; // resolve的結果依舊是promise 那就繼續解析 resolvePromise(promise2, y, resolve, reject); }, err => { // 成功和失敗只能調用一個 if (called) return; called = true; reject(err);// 失敗了就失敗了 }) } else { resolve(x); // 直接成功便可 } } catch (e) { // 也屬於失敗 if (called) return; called = true; // 取then出錯了那就不要在繼續執行了 reject(e); } } else { resolve(x); } }
解決其餘問題
一、祕籍規定onFulfilled,onRejected都是可選參數,若是他們不是函數,必須被忽略
• onFulfilled返回一個普通的值,成功時直接等於 value => value
• onRejected返回一個普通的值,失敗時若是直接等於 value => value,則會跑到下一個then中的onFulfilled中,因此直接扔出一個錯誤 reason => throw err 二、祕籍規定onFulfilled或onRejected不能同步被調用,必須異步調用。咱們就用setTimeout解決異步問題
•若是onFulfilled或onRejected報錯,則直接返回reject()
class Promise{ constructor(executor){ this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } }; try{ executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled,onRejected) { // onFulfilled若是不是函數,就忽略onFulfilled,直接返回value onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; // onRejected若是不是函數,就忽略onRejected,直接扔出錯誤 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 (e) { reject(e); } }, 0); }; if (this.state === 'rejected') { // 異步 setTimeout(() => { // 若是報錯 try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }; if (this.state === 'pending') { this.onResolvedCallbacks.push(() => { // 異步 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.onRejectedCallbacks.push(() => { // 異步 setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0) }); }; }); // 返回promise,完成鏈式 return promise2; } }
順便附贈catch和resolve、reject、race、all方法
class Promise{ constructor(executor){ this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } }; try{ executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled,onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; 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 (e) { reject(e); } }, 0); }; if (this.state === 'rejected') { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }; if (this.state === 'pending') { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0) }); }; }); return promise2; } catch(fn){ return this.then(null,fn); } } function resolvePromise(promise2, x, resolve, reject){ if(x === promise2){ return reject(new TypeError('Chaining cycle detected for promise')); } let called; if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { let then = x.then; if (typeof then === 'function') { then.call(x, y => { if(called)return; called = true; resolvePromise(promise2, y, resolve, reject); }, err => { if(called)return; called = true; reject(err); }) } else { resolve(x); } } catch (e) { if(called)return; called = true; reject(e); } } else { resolve(x); } } //resolve方法 Promise.resolve = function(val){ return new Promise((resolve,reject)=>{ resolve(val) }); } //reject方法 Promise.reject = function(val){ return new Promise((resolve,reject)=>{ reject(val) }); } //race方法 Promise.race = function(promises){ return new Promise((resolve,reject)=>{ for(let i=0;i<promises.length;i++){ promises[i].then(resolve,reject) }; }) } //all方法(獲取全部的promise,都執行then,把結果放到數組,一塊兒返回) Promise.all = function(promises){ let arr = []; let i = 0; function processData(index,data){ arr[index] = data; i++; if(i == promises.length){ resolve(arr); }; }; return new Promise((resolve,reject)=>{ for(let i=0;i<promises.length;i++){ promises[i].then(data=>{ processData(i,data); },reject); }; }); }
如何驗證咱們的promise是否正確
一、先在後面加上下述代碼
二、npm 有一個promises-aplus-tests插件 npm i promises-aplus-tests -g 能夠全局安裝 mac用戶最前面加上sudo
三、命令行 promises-aplus-tests [js文件名] 便可驗證
// 目前是經過他測試 他會測試一個對象 // 語法糖 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 符不符合promisesA+規範