Promise是前端面試中的高頻問題,我做爲面試官的時候,問Promise的機率超過90%,據我所知,大多數公司,都會問一些關於Promise的問題。若是你能根據PromiseA+的規範,寫出符合規範的源碼,那麼我想,對於面試中的Promise相關的問題,都可以給出比較完美的答案。javascript
個人建議是,對照規範多寫幾回實現,也許第一遍的時候,是改了屢次,才能經過測試,那麼須要反覆的寫,我已經將Promise的源碼實現寫了不下七遍。前端
/** * 1. new Promise時,須要傳遞一個 executor 執行器,執行器馬上執行 * 2. executor 接受兩個參數,分別是 resolve 和 reject * 3. promise 只能從 pending 到 rejected, 或者從 pending 到 fulfilled * 4. promise 的狀態一旦確認,就不會再改變 * 5. promise 都有 then 方法,then 接收兩個參數,分別是 promise 成功的回調 onFulfilled, * 和 promise 失敗的回調 onRejected * 6. 若是調用 then 時,promise已經成功,則執行 onFulfilled,並將promise的值做爲參數傳遞進去。 * 若是promise已經失敗,那麼執行 onRejected, 並將 promise 失敗的緣由做爲參數傳遞進去。 * 若是promise的狀態是pending,須要將onFulfilled和onRejected函數存放起來,等待狀態肯定後,再依次將對應的函數執行(發佈訂閱) * 7. then 的參數 onFulfilled 和 onRejected 能夠缺省 * 8. promise 能夠then屢次,promise 的then 方法返回一個 promise * 9. 若是 then 返回的是一個結果,那麼就會把這個結果做爲參數,傳遞給下一個then的成功的回調(onFulfilled) * 10. 若是 then 中拋出了異常,那麼就會把這個異常做爲參數,傳遞給下一個then的失敗的回調(onRejected) * 11.若是 then 返回的是一個promise,那麼須要等這個promise,那麼會等這個promise執行完,promise若是成功, * 就走下一個then的成功,若是失敗,就走下一個then的失敗 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function Promise(executor) { let self = this; self.status = PENDING; self.onFulfilled = [];//成功的回調 self.onRejected = []; //失敗的回調 //PromiseA+ 2.1 function resolve(value) { if (self.status === PENDING) { self.status = FULFILLED; self.value = value; self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1 } } function reject(reason) { if (self.status === PENDING) { self.status = REJECTED; self.reason = reason; self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2 } } try { executor(resolve, reject); } catch (e) { reject(e); } } Promise.prototype.then = function (onFulfilled, onRejected) { //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; let self = this; //PromiseA+ 2.2.7 let promise2 = new Promise((resolve, reject) => { if (self.status === FULFILLED) { //PromiseA+ 2.2.2 //PromiseA+ 2.2.4 --- setTimeout setTimeout(() => { try { //PromiseA+ 2.2.7.1 let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { //PromiseA+ 2.2.7.2 reject(e); } }); } else if (self.status === REJECTED) { //PromiseA+ 2.2.3 setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else if (self.status === PENDING) { self.onFulfilled.push(() => { setTimeout(() => { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }); self.onRejected.push(() => { setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }); } }); return promise2; } function resolvePromise(promise2, x, resolve, reject) { let self = this; //PromiseA+ 2.3.1 if (promise2 === x) { reject(new TypeError('Chaining cycle')); } if (x && typeof x === 'object' || typeof x === 'function') { let used; //PromiseA+2.3.3.3.3 只能調用一次 try { let then = x.then; if (typeof then === 'function') { //PromiseA+2.3.3 then.call(x, (y) => { //PromiseA+2.3.3.1 if (used) return; used = true; resolvePromise(promise2, y, resolve, reject); }, (r) => { //PromiseA+2.3.3.2 if (used) return; used = true; reject(r); }); }else{ //PromiseA+2.3.3.4 if (used) return; used = true; resolve(x); } } catch (e) { //PromiseA+ 2.3.3.2 if (used) return; used = true; reject(e); } } else { //PromiseA+ 2.3.3.4 resolve(x); } } module.exports = Promise;
有專門的測試腳本能夠測試所編寫的代碼是否符合PromiseA+的規範。java
首先,在promise實現的代碼中,增長如下代碼:git
Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
安裝測試腳本:es6
npm install -g promises-aplus-tests
若是當前的promise源碼的文件名爲promise.jsgithub
那麼在對應的目錄執行如下命令:面試
promises-aplus-tests promise.js
promises-aplus-tests中共有872條測試用例。以上代碼,能夠完美經過全部用例。shell
對上面的代碼實現作一點簡要說明(其它一些內容註釋中已經寫得很清楚):npm
PS: 下面是我翻譯的規範,供參考數組
術語
要求
Promise 必須處於如下三個狀態之一: pending, fulfilled 或者是 rejected
2.1.1.1 能夠變成 fulfilled 或者是 rejected
2.1.2.1 不會變成其它狀態 2.1.2.2 必須有一個value值
2.1.3.1 不會變成其它狀態 2.1.3.2 必須有一個promise被reject的reason
歸納便是:promise的狀態只能從pending變成fulfilled,或者從pending變成rejected.promise成功,有成功的value.promise失敗的話,有失敗的緣由
promise必須提供一個then方法,來訪問最終的結果
promise的then方法接收兩個參數
promise.then(onFulfilled, onRejected)
2.2.1.1 onFulfilled 必須是函數類型 2.2.1.2 onRejected 必須是函數類型
2.2.2.1 必須在promise變成 fulfilled 時,調用 onFulfilled,參數是promise的value 2.2.2.2 在promise的狀態不是 fulfilled 以前,不能調用 2.2.2.3 onFulfilled 只能被調用一次
2.2.3.1 必須在promise變成 rejected 時,調用 onRejected,參數是promise的reason 2.2.3.2 在promise的狀態不是 rejected 以前,不能調用 2.2.3.3 onRejected 只能被調用一次
2.2.6.1 若是promise變成了 fulfilled態,全部的onFulfilled回調都須要按照then的順序執行 2.2.6.2 若是promise變成了 rejected態,全部的onRejected回調都須要按照then的順序執行
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 onFulfilled 或 onRejected 執行的結果爲x,調用 resolvePromise 2.2.7.2 若是 onFulfilled 或者 onRejected 執行時拋出異常e,promise2須要被reject 2.2.7.3 若是 onFulfilled 不是一個函數,promise2 以promise1的值fulfilled 2.2.7.4 若是 onRejected 不是一個函數,promise2 以promise1的reason rejected
resolvePromise(promise2, x, resolve, reject)
2.3.2.1 若是x是pending態,那麼promise必需要在pending,直到 x 變成 fulfilled or rejected. 2.3.2.2 若是 x 被 fulfilled, fulfill promise with the same value. 2.3.2.3 若是 x 被 rejected, reject promise with the same reason.
2.3.3.1 let then = x.then. 2.3.3.2 若是 x.then 這步出錯,那麼 reject promise with e as the reason.. 2.3.3.3 若是 then 是一個函數,then.call(x, resolvePromiseFn, rejectPromise) 2.3.3.3.1 resolvePromiseFn 的 入參是 y, 執行 resolvePromise(promise2, y, resolve, reject); 2.3.3.3.2 rejectPromise 的 入參是 r, reject promise with r. 2.3.3.3.3 若是 resolvePromise 和 rejectPromise 都調用了,那麼第一個調用優先,後面的調用忽略。 2.3.3.3.4 若是調用then拋出異常e 2.3.3.3.4.1 若是 resolvePromise 或 rejectPromise 已經被調用,那麼忽略 2.3.3.3.4.3 不然,reject promise with e as the reason 2.3.3.4 若是 then 不是一個function. fulfill promise with x.
雖然上述的promise源碼已經符合PromiseA+的規範,可是原生的Promise還提供了一些其餘方法,如:
下面具體說一下每一個方法的實現:
Promise.resolve
Promise.resolve(value) 返回一個以給定值解析後的Promise 對象.
Promise.resolve = function (param) { if (param instanceof Promise) { return param; } return new Promise((resolve, reject) => { if (param && param.then && typeof param.then === 'function') { setTimeout(() => { param.then(resolve, reject); }); } else { resolve(param); } }); }
thenable對象的執行加 setTimeout的緣由是根據原生Promise對象執行的結果推斷的,以下的測試代碼,原生的執行結果爲: 20 400 30;爲了一樣的執行順序,增長了setTimeout延時。
測試代碼:
let p = Promise.resolve(20); p.then((data) => { console.log(data); }); let p2 = Promise.resolve({ then: function(resolve, reject) { resolve(30); } }); p2.then((data)=> { console.log(data) }); let p3 = Promise.resolve(new Promise((resolve, reject) => { resolve(400) })); p3.then((data) => { console.log(data) });
Promise.reject
Promise.reject方法和Promise.resolve不一樣,Promise.reject()方法的參數,會原封不動地做爲reject的理由,變成後續方法的參數。
Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason); }); }
Promise.prototype.catch
Promise.prototype.catch 用於指定出錯時的回調,是特殊的then方法,catch以後,能夠繼續 .then
Promise.prototype.catch = function (onRejected) { return this.then(null, onRejected); }
Promise.prototype.finally
無論成功仍是失敗,都會走到finally中,而且finally以後,還能夠繼續then。而且會將值原封不動的傳遞給後面的then.
Promise.prototype.finally = function (callback) { return this.then((value) => { return Promise.resolve(callback()).then(() => { return value; }); }, (err) => { return Promise.resolve(callback()).then(() => { throw err; }); }); }
Promise.all
Promise.all(promises) 返回一個promise對象
Promise.all = function (promises) { return new Promise((resolve, reject) => { let index = 0; let result = []; if (promises.length === 0) { resolve(result); } else { function processValue(i, data) { result[i] = data; if (++index === promises.length) { resolve(result); } } for (let i = 0; i < promises.length; i++) { //promises[i] 多是普通值 Promise.resolve(promises[i]).then((data) => { processValue(i, data); }, (err) => { reject(err); return; }); } } }); }
測試代碼:
var promise1 = new Promise((resolve, reject) => { resolve(3); }) var promise2 = 42; var promise3 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, 'foo'); }); Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); //[3, 42, 'foo'] },(err)=>{ console.log(err) }); var p = Promise.all([]); // will be immediately resolved var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously console.log(p); console.log(p2) setTimeout(function(){ console.log('the stack is now empty'); console.log(p2); });
Promise.race
Promise.race函數返回一個 Promise,它將與第一個傳遞的 promise 相同的完成方式被完成。它能夠是完成( resolves),也能夠是失敗(rejects),這要取決於第一個完成的方式是兩個中的哪一個。
若是傳的參數數組是空,則返回的 promise 將永遠等待。
若是迭代包含一個或多個非承諾值和/或已解決/拒絕的承諾,則 Promise.race 將解析爲迭代中找到的第一個值。
Promise.race = function (promises) { return new Promise((resolve, reject) => { if (promises.length === 0) { return; } else { for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then((data) => { resolve(data); return; }, (err) => { reject(err); return; }); } } }); }
測試代碼:
Promise.race([ new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }), undefined, new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) }) ]).then((data) => { console.log('success ', data); }, (err) => { console.log('err ',err); }); Promise.race([ new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }), new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }), new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) }) ]).then((data) => { console.log(data); }, (err) => { console.log(err); });
謝謝您花費寶貴的時間閱讀本文,若是本文給了您一點幫助或者是啓發,那麼不要吝嗇你的贊和Star哈,您的確定是我前進的最大動力。https://github.com/YvetteLau/...
推薦關注本人公衆號: