做爲程序猿,能夠沒有女友(好像也只能接收找不到的現實),但絕對不能沒有格子衫、護髮素····和對原生的摯愛,今天,我們就來手寫一個Promise吧
看到掘金裏有一篇文章,45道Promise面試,讀完以爲很好,也正是由於寫完這些題目以爲本身深深的不足,才促成了本文的出現,表示衷心感謝;你們也能夠去試試看,本身到底掌握程度如何(看完本文,代碼層面解決此文章的全部題目,反正本菜已經解決了,皮~)java
實現一個符合 Promise A+ 規範的我的版promise(經過promises-aplus-tests測試)node
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {
console.log('then:', res);
}).catch((err) => {
console.log('catch:', err);
})
複製代碼
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
複製代碼
不照搬照套定義,簡言之,由於Promise已經是已經是業內通用,因此必然要保證在使用你實現的promise的過程當中行爲的可靠性,其實也就是一個【面向接口編程】的感受吧,標準化才穩定,官話建議點進連接看看,小菜整理後面試
promises-aplus-tests
進行校驗,文末會詳細講解const PENDING = 'PENDING' // 等待態
const FULFILLED = 'FULFILLED' // 成功態
const REJECTED = 'REJECTED' /
複製代碼
// 只有狀態是等待態的時候 才能夠更新狀態
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
複製代碼
onFulfilled
和 失敗的回調onRejected
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
複製代碼
const PENDING = 'PENDING' // 等待態
const FULFILLED = 'FULFILLED' // 成功態
const REJECTED = 'REJECTED' //
class Promise {
constructor(executor) {
this.status = PENDING; // 默認是等待態
this.value = undefined; // 用於記錄成功的數據
this.reason = undefined; // 用於記錄失敗的緣由
// 只有狀態是等待態的時候 才能夠更新狀態
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
// executor 執行的時候 須要傳入兩個參數,給用戶來改變狀態的
try {
executor(resolve, reject);
} catch (e) { // 表示當前有異常,那就使用這個異常做爲promise失敗的緣由
reject(e)
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
module.exports = Promise
複製代碼
會輸出 fail reason
重點想下爲何不是fail 我失敗了
要理解非pengding狀態下不會改變任何東西ajax
let Promise = require('./promise')
let promise = new Promise((resolve,reject)=>{
reject('reason');
resolve('value')
throw new Error('我失敗了');
})
promise.then((success)=>{
console.log('success',success)
},(err)=>{
console.log('fail',err)
});
複製代碼
當executor執行異步邏輯時,會存在then方法調用時狀態仍是pengding的狀況,由於異步的回調會入異步隊列中,後於同步代碼執行。spring
this.onResolvedCallbacks = []; // 存放成功時的回調
this.onRejectedCallbacks = []; // 存放失敗時的回調
複製代碼
if(this.status === PENDING){ // 發佈訂閱
this.onResolvedCallbacks.push(()=>{
// TODO ...
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason)
})
}
複製代碼
// 只有狀態是等待態的時候 才能夠更新狀態
let resolve = (value) => {
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());
}
}
複製代碼
let Promise = require('./promise')
let promise = new Promise((resolve,reject)=>{
setTimeout(() => { // 異步的
resolve('value') //此時若是調用了resolve 就讓剛纔存儲的成功的回調函數去執行
}, 1000);
})
// 同一個promise實例 能夠then屢次
// 核心就是發佈訂閱模式
promise.then((success)=>{ // 若是調用then的時候沒有成功也沒有失敗。我能夠先保存成功和失敗的回調
console.log('success',success)
},(err)=>{
console.log('fail',err)
});
promise.then((success)=>{
console.log('success',success)
},(err)=>{
console.log('fail',err)
});
複製代碼
挺簡單的,該寫下then方法,new一個新的promise返回就能夠了npm
then(onFulfilled, onRejected) {
// 可選參數的處理
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}
// 遞歸
let promise2 = new Promise((resolve, reject) => {
# 原有邏輯
})
return promise2;
}
複製代碼
這是關鍵,特色再加個xmind 編程
then(onFulfilled, onRejected) {
// 可選參數的處理
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}
// 遞歸
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === 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;
}
複製代碼
const resolvePromise = (promise2, x, resolve, reject) => {
// 判斷 可能你的promise要和別人的promise來混用
// 可能不一樣的promise庫之間要相互調用
if (promise2 === x) { // x 若是和promise2 是同一我的 x 永遠不能成功或者失敗,因此就卡死了,咱們須要直接報錯便可
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
// ------ 咱們要判斷x的狀態 判斷x 是否是promise-----
// 1.先判斷他是否是對象或者函數
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// x 須要是一個對象或者是函數
let called; // 爲了考慮別人promise 不健壯因此咱們須要本身去判斷一下,若是調用失敗不能成功,調用成功不能失敗,不能屢次調用成功或者失敗
try{
let then = x.then; // 取出then方法 這個then方法是採用defineProperty來定義的
if(typeof then === 'function'){
// 判斷then是否是一個函數,若是then 不是一個函數 說明不是promise
// 只能認準他是一個promise了
then.call(x, y =>{ // 若是x是一個promise 就採用這個promise的返回結果
if(called) return;
called = true
resolvePromise(promise2, y, resolve, reject); // 繼續解析成功的值
},r=>{
if(called) return;
called = true
reject(r); // 直接用r 做爲失敗的結果
})
}else{
// x={then:'123'}
resolve(x);
}
}catch(e){
if(called) return;
called = true
reject(e); // 去then失敗了 直接觸發promise2的失敗邏輯
}
} else {
// 確定不是promise
resolve(x); // 直接成功便可
}
}
複製代碼
console.log('-------------- my ---------------')
// 宏
const PENDING = 'PENDING' // 等待態
const FULFILLED = 'FULFILLED' // 成功態
const REJECTED = 'REJECTED' //
const resolvePromise = (promise2, x, resolve, reject) => {
// 判斷 可能你的promise要和別人的promise來混用
// 可能不一樣的promise庫之間要相互調用
if (promise2 === x) { // x 若是和promise2 是同一我的 x 永遠不能成功或者失敗,因此就卡死了,咱們須要直接報錯便可
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
// ------ 咱們要判斷x的狀態 判斷x 是否是promise-----
// 1.先判斷他是否是對象或者函數
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// x 須要是一個對象或者是函數
let called; // 爲了考慮別人promise 不健壯因此咱們須要本身去判斷一下,若是調用失敗不能成功,調用成功不能失敗,不能屢次調用成功或者失敗
try{
let then = x.then; // 取出then方法 這個then方法是採用defineProperty來定義的
if(typeof then === 'function'){
// 判斷then是否是一個函數,若是then 不是一個函數 說明不是promise
// 只能認準他是一個promise了
then.call(x, y =>{ // 若是x是一個promise 就採用這個promise的返回結果
if(called) return;
called = true
resolvePromise(promise2, y, resolve, reject); // 繼續解析成功的值
},r=>{
if(called) return;
called = true
reject(r); // 直接用r 做爲失敗的結果
})
}else{
// x={then:'123'}
resolve(x);
}
}catch(e){
if(called) return;
called = true
reject(e); // 去then失敗了 直接觸發promise2的失敗邏輯
}
} else {
// 確定不是promise
resolve(x); // 直接成功便可
}
}
class Promise {
constructor(executor) {
this.status = PENDING; // 默認是等待態
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = []; // 存放成功時的回調
this.onRejectedCallbacks = []; // 存放失敗時的回調
let resolve = (value) => {
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 { // try + catch 只能捕獲同步異常
executor(resolve, reject);
} catch (e) {
console.log(e);
reject(e)
}
}
// 只要x 是一個普通值 就會讓下一個promise變成成功態
// 這個x 有多是一個promise,我須要採用這個promise的狀態
then(onFulfilled, onRejected) {
// 可選參數的處理
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}
// 遞歸
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === 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;
}
}
Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
module.exports = Promise;
複製代碼
promisesaplus.com/(即Promise A+) 是一個介紹promise如何實現的一個網站,並提供了一個promises-aplus-tests的npm全局包用於測試,簡單三步走設計模式
npm install promises-aplus-tests -g
Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
複製代碼
promises-aplus-tests promise.js
直接上代碼啦,相信能堅持到如今的小夥伴確定比本南方小菜強,看代碼秒懂系列數組
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let results = []; let i = 0;
function processData(index, data) {
results[index] = data; // let arr = [] arr[2] = 100
if (++i === promises.length) {
resolve(results);
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
p.then((data) => { // 成功後把結果和當前索引 關聯起來
processData(i, data);
}, reject);
}
})
}
複製代碼
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
p.then(resolve, reject);
}
})
}
複製代碼
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
複製代碼
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
resolve(value);
})
}
複製代碼
Promise.prototype.catch = function (onrejected) {
return this.then(null, onrejected)
}
複製代碼
知道原理懼怕面試題嗎?來吧promise
then success1
new Promise((resolve, reject) => {
resolve(1)
}).then(2)
.then(Promise.resolve(3))
.then(console.log)
複製代碼
2. 此處resolve用戶傳了個普通值/對象,當傳非函數時,promise內部忽略傳遞值並傳一個默認函數(貼心的截個圖,val=>val的形式,不過仍是建議若是不熟悉去看看第三版)值傳遞
複製代碼
new Promise((resolve, reject) => {
resolve(1)
}).then(val=>val)
.then(val=>val)
.then(console.log)
複製代碼
3. 初版就能夠看到,在最開始的resolve(1)時,內部屬性this.val已是1了,後面均無改變val的操做,故最後一次傳來一個log函數,就打印出來了
4. 答案:1
複製代碼
恭喜您堅持下來了,建議能夠本身跑一跑,畢竟測試用例都寫了,如今,再去試試本身的實力吧~~,再次表示感謝45道Promise面試
不少時候,不只要知其然,還要知其因此然;儘管咱們在現在資源豐富、文檔普及的時代,學會一個工具的使用仍是很快的,成爲一個cv工程師很簡單,但也就很簡單的失去了擁有那種計算機行業的快樂的能力,【幹一行愛一行】纔是最關鍵的碼農精神所在;