① Promise 是一個類或者函數,內部擁有3個狀態,分別爲pending(等待)、fulfilled(執行、完成)、rejected(拒絕、未完成)。
默認爲pending狀態,即Promise對象剛建立的時候狀態爲pending,而且pending狀態能夠轉換fulfilled或者rejected。
fulfilled和rejected爲最終的狀態,一旦變爲fulfilled或者rejected,那麼將沒法轉變爲其餘狀態。promise
② Promise須要對外提供一個then方法。異步
promise.then(onFulfilled, onRejected)
若是可選參數onFulfilled和onRejected不爲函數時應該被忽略;函數
onFulfilled和onRejected函數都應該是異步執行的;this
當調用 onFulfilled 函數時,會將當前 Promise 的值做爲參數傳入,而且只能調用一次;code
當調用 onRejected 函數時,會將當前 Promise 的失敗緣由做爲參數傳入,而且只能調用一次;對象
then函數的返回值仍然爲Promise,以便進行鏈式調用;遞歸
③ resolvePromise
then方法會建立並返回一個Promise對象,then中註冊的回調函數會返回各類值,必須進行校驗。回調函數
then方法返回的promise不能與then中回調函數返回值x相等,不然須要拋出錯誤;源碼
若是是then回調函數返回值爲一個非Promise對象,則直接用then返回的promise對象的resolve方法,resolve(x)便可。it
若是then回調函數返回值x爲一個Promise對象或者一個帶then方法的對象或函數,那麼須要執行其then方法註冊回調,拿到Promise或類Promise對象的值做爲then返回的promise的值,若是值仍然爲Promise對象則須要進行遞歸操做;
① 根據第一條規範,Promise是一個類或者函數,因此咱們先將Promise定義成一個類,同時內部有三個狀態,咱們將其定義爲常量。
var PENDING = "pending"; // 等待狀態 var FULFILLED = "fulfilled"; // 執行、完成狀態 var REJECTED = "rejected"; // 拒絕、未完成狀態 class Promise { constructor() { this.state = PENDING; // Promise對象建立完成後默認爲等待狀態 } }
② 咱們在建立Promise的時候會傳入一個函數,該函數會在建立Promise對象的時候當即執行,而且會接收兩個參數,分別用於執行或拒絕當前Promise對象,即修改當前Promise對象的狀態。Promise是用於處理異步的,因此在Promise狀態變爲完成的時候可能會接收到異步操做執行的結果,在Promise狀態變爲未完成的時候可能會接收到失敗的緣由,因此Promise內部還須要保存異步操做的結果value、失敗的緣由reason。
...... class Promise { constructor(executor) { // 傳入執行器函數 ...... this.value = undefined; // 保存異步操做的結果 this.reason = undefined; // 保存失敗的緣由 const resolve = (value) => { this.value = value; this.state = FULFILLED; // 將Promise對象的狀態改成完成狀態 } const reject = (reason) => { this.reason = reason; this.state = REJECTED; // 將Promise對象的狀態改成未完成狀態 } try { executor(resolve, reject); // 執行器由用戶傳入可能會發生錯誤,因此須要進行捕獲 } catch(e) { reject(e); } } }
③ 這裏還存在一個問題,就是Promise必須是單次執行的,Promise的狀態一旦從pending狀態修改成fulfilled或者rejected,就不能再發生變化,從fulfilled變爲fulfilled也不能夠,也就是說resolve或者reject只能執行一次。因此咱們須要對resolve和reject內部進行判斷,若是狀態已經變化了則再也不執行了,如:
...... class Promise { constructor(executor) { // 傳入執行器函數 ...... const resolve = (value) => { if (this.state === PENDING) { // 防止用戶屢次resolve,以第一次resolve爲準 ...... } } const reject = (reason) => { if (this.state === PENDING) { // 防止用戶屢次reject ...... } } ...... } }
④ 給Promise添加一個then函數,then函數接收onFulfilled, onRejected兩個函數做爲參數,分別用於處理Promise完成時和未完成時的回調函數,若是不是函數,則要進行初始化爲一個函數,如:
class Promise { then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { // 若是onFulfilled不是函數,則初始化一個完成處理函數 return value; }; onRejected = typeof onRejected === "function" ? onRejected : (reason) => { // 若是onRejected不是函數,則初始化一個未完成處理函數 throw reason; // 傳什麼就拋出什麼 } } }
⑤ then方法其實就是一個註冊回調的過程,當調用then的這個Promise對象的狀態變爲完成狀態就能夠執行onFulfilled回調函數,當Promise對象的狀態變爲拒絕狀態就能夠執行onRejected回調函數了。因此回調函數的執行依賴於調用then的Promise的狀態。同時爲了支持鏈式調用,then方法還須要返回一個Promise對象。根據前面的Promise規範,傳入的回調函數必須異步執行,這裏用setTimeout進行模擬。
class Promise { then(onFulfilled, onRejected) { ...... let promise; switch(this.state) { case FULFILLED: // 調用then方法的時候,當前Promise狀態已經變成完成狀態,則可用當即執行完成的回調函數 promise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(this.value); } catch(e) { console.log(e); // 打印錯誤信息 reject(e); } }); }); break; case REJECTED: promise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(this.reason); } catch(e) { reject(e); } }); } break; case PENDING: promise = new Promise((resolve, reject) => { // TODO }); break; } return promise; } }
⑥ 當調用then的Promise對象處於pending狀態的時候,此時經過then註冊的回調函數不能當即執行,必須等待Promise的狀態變爲最終狀態才能執行註冊的回調函數。這裏就涉及到了一個發佈訂閱模式。咱們能夠先將回調函數保存起來,那麼何時Promise纔會變成最終狀態呢?那就是調用resolve或reject的時候,因此咱們能夠在調用resolve或reject的時候,取出註冊的回調函數而後執行便可。
class Promise { constructor(executor) { const resolve = (value) => { if (this.state === PENDING) { // 防止用戶屢次resolve,以第一次resolve爲準 ...... this.onFulfilleds.forEach(fn => fn()); // 取出then中註冊的完成回調函數並執行 } }; const reject = (reason) => { if (this.state === PENDING) { // 防止用戶屢次reject ...... this.onRejecteds.forEach(fn => fn()); // 取出then中註冊的拒絕回調函數並執行 } }; } then(onFulfilled, onRejected) { ...... switch(this.state) { case PENDING: promise = new Promise((resolve, reject) => { this.onFulfilleds.push(() => { try { let x = onFulfilled(this.value); } catch(e) { console.log(e); // 打印錯誤信息 reject(e); } }); this.onRejecteds.push(() => { try { let x = onRejected(this.reason); } catch(e) { reject(e); } }); }); break; } } }
⑦ 接下來就是要處理then註冊的回調函數的返回值了,由於回調函數的返回值多是各類各樣的狀況,多是普通的值,多是Promise對象,也多是帶then方法的對象,因此咱們要一一進行處理。這裏咱們使用一個單獨的方法resolvePromise()進行各類狀況的處理,如:
// 傳入then()方法中建立的Promise對象,回調函數的返回值x,then()方法中建立的Promise的resolve、reject const resolvePromise = function(promise, x, resolve, reject) { // TODO } class Promise { constructor(executor) { // 傳入執行器函數 ...... } then(onFulfilled, onRejected) { case FULFILLED: promise = new Promise((resolve, reject) => { ...... let x = onFulfilled(this.value); resolvePromise(promise, x, resolve, reject); // 處理回調函數的返回值 }); case REJECTED: promise = new Promise((resolve, reject) => { ...... let x = onRejected(this.reason); resolvePromise(promise, x, resolve, reject); // 處理回調函數的返回值 }); case PENDING: this.onFulfilleds.push(() => { let x = onFulfilled(this.value); resolvePromise(promise, x, resolve, reject); // 處理回調函數的返回值 }); this.onRejecteds.push(() => { let x = onRejected(this.reason); resolvePromise(promise, x, resolve, reject); // 處理回調函數的返回值 }); } }
① 若是回調函數返回值與then()方法中建立的Promise對象相同則拋出錯誤,這至關因而本身等本身會進入死循環。
let p1 = new Promise((resolve, reject) => { resolve(1); }) let p2 = p1.then((value) => { // p2即then方法內建立Promise對象 return p2; }); // 結果拋出錯誤,顯示Chaining cycle detected for promise #<Promise>
const resolvePromise = function(promise, x, resolve, reject) { if (promise === x) { // 禁止resolve本身 throw new Error("Chaining cycle detected for promise #<Promise>"); } }
② 若是回調函數返回的是一個Promise對象或者帶then方法的類Promise對象,又或者一個函數,由於函數上也可能有then方法,那麼咱們須要取出then方法並執行,對於Promise對象而言,then方法的執行就會註冊相應的回調函數,等Promise狀態變爲最終狀態後就會執行對應的回調函數,回調函數執行後就能夠拿到Promise對象的value值,而後將該value值做爲調用then方法建立的Promise的對象的value值。
const resolvePromise = function(promise, x, resolve, reject) { ...... if ((x && typeof x === "object") || typeof x === "function") { // 若是是對象或者函數,函數也可能有then方法 let executed; try { let then = x.then; // 嘗試取出then方法 if (typeof then === "function") { // 若是該對象上存在then方法,那麼是個Promise對象或者包含then方法的對象 then.call(x, function (y) { // 執行then方法,對於真正的Promise對象,則會註冊回調,等到狀態變化後,回調函數會執行,回調中能接收到Promise的value值 if (executed) return; executed = true; // 註冊的回調函數只能執行一次 resolvePromise(promise, y, resolve, reject); // 返回值還多是一個Promise對象,故須要遞歸直到變爲普通值爲止 }, function (e) { if (executed) return; executed = true; reject(e); }); } else { // 不包含then方法的普通對象,直接resolve便可 resolve(x); } } catch(e) { if (executed) return; executed = true; reject(e); } } else { resolve(x); } }
catch能夠看作是一個特殊的then方法,其內部會調用then()方法,可是僅註冊拒絕的回調函數,這也就是then(onFulfilled, onRejected)和then(onFulfilled).catch(onRejected)的區別,若是將onRejected寫到then中,那麼當then的onFulfilled發生錯誤的時候,onRejected就沒法捕獲到其中的錯誤,而寫到catch中,那麼就至關因而下一個then()方法,故能捕獲到上一個then()方法中發生的錯誤。
class Promise { catch(onRejected) { return this.then(null, onRejected); // 僅註冊拒絕的回調函數 } }
Promise其實就是一個類,內部有state、value、reason等屬性,分別用於存儲當前Promise的狀態、執行成功後的返回值,執行失敗的緣由,同時內部還提供了resolve、reject兩個方法,這兩個方法會以參數的形式傳遞給執行器,即傳遞到外部,以便修改Promise的狀態。
Promise還提供了一個then方法用於註冊回調函數,註冊回調的時候與當前Promise的狀態有關,若是是最終狀態,則當即執行,若是是等待狀態,則先保存起來,等到調用resolve或reject方法的時候再取出回調並執行。註冊的回調函數可能會返回各類各樣的值:
若是返回的是普通值,那麼直接用then返回的Promise的resolve方法resolve便可;
若是返回的是Promise對象或者是帶then方法的對象或函數,那麼須要調用其then方法並註冊一個自定義回調用於接收當前Promise的值,等該Promise變爲最終狀態後會執行回調就能夠拿到其value,最後將其做爲then返回的Promise的value,即resolve(x)。
完整源碼以下:
var PENDING = "pending"; // 等待狀態 var FULFILLED = "fulfilled"; // 執行、完成狀態 var REJECTED = "rejected"; // 拒絕、未完成狀態 // 傳入then()方法中建立的Promise對象,回調函數的返回值x,then()方法中建立的Promise的resolve、reject const resolvePromise = function(promise, x, resolve, reject) { if (promise === x) { // 禁止resolve本身 throw new Error("Chaining cycle detected for promise #<Promise>"); } if ((x && typeof x === "object") || typeof x === "function") { // 若是是對象或者函數,函數也可能有then方法 let executed; try { let then = x.then; // 嘗試取出then方法 if (typeof then === "function") { // 若是該對象上存在then方法,那麼是個Promise對象或者包含then方法的對象 then.call(x, function (y) { // 執行then方法,對於真正的Promise對象,則會註冊回調,等到狀態變化後,回調函數會執行,回調中能接收到Promise的value值 if (executed) return; executed = true; // 註冊的回調函數只能執行一次 resolvePromise(promise, y, resolve, reject); // 返回值還多是一個Promise對象,故須要遞歸直到變爲普通值爲止 }, function (e) { if (executed) return; executed = true; reject(e); }); } else { // 不包含then方法的普通對象,直接resolve便可 resolve(x); } } catch(e) { if (executed) return; executed = true; reject(e); } } else { resolve(x); } } class Promise { constructor(executor) { // 傳入執行器函數 this.state = PENDING; // Promise對象建立完成後默認爲等待狀態 this.value = undefined; // 保存異步操做的結果 this.reason = undefined; // 保存失敗的緣由 this.onFulfilleds = []; // 保存then中註冊的完成回調函數 this.onRejecteds = []; // 保存then中註冊的拒絕回調函數 const resolve = (value) => { if (this.state === PENDING) { // 防止用戶屢次resolve,以第一次resolve爲準 this.value = value; this.state = FULFILLED; // 將Promise對象的狀態改成完成狀態 this.onFulfilleds.forEach(fn => fn()); // 取出then中註冊的完成回調函數並執行 } }; const reject = (reason) => { if (this.state === PENDING) { // 防止用戶屢次reject this.reason = reason; this.state = REJECTED; // 將Promise對象的狀態改成未完成狀態 this.onRejecteds.forEach(fn => fn()); // 取出then中註冊的拒絕回調函數並執行 } }; try { executor(resolve, reject); // 執行器由用戶傳入可能會發生錯誤,因此須要進行捕獲 } catch(e) { reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { // 若是onFulfilled不是函數,則初始化一個完成處理函數 return value; }; onRejected = typeof onRejected === "function" ? onRejected : (reason) => { // 若是onRejected不是函數,則初始化一個未完成處理函數 throw reason; // 傳什麼就拋出什麼 } let promise; switch(this.state) { case FULFILLED: // 調用then方法的時候,當前Promise狀態已經變成完成狀態,則可用當即執行完成的回調函數 promise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise, x, resolve, reject); } catch(e) { console.log(e); reject(e); } }); }); break; case REJECTED: promise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise, x, resolve, reject); } catch(e) { reject(e); } }); }); break; case PENDING: promise = new Promise((resolve, reject) => { this.onFulfilleds.push(() => { try { let x = onFulfilled(this.value); resolvePromise(promise, x, resolve, reject); } catch(e) { reject(e); } }); this.onRejecteds.push(() => { try { let x = onRejected(this.reason); resolvePromise(promise, x, resolve, reject); } catch(e) { reject(e); } }); }); break; } return promise; } catch(onRejected) { return this.then(null, onRejected); // 僅註冊拒絕的回調函數 } }