進來看看ES6 Promise最全手寫實現

寫在開頭

  • 這幾天看到有些技術羣在發Promise相關的一些實現,本身最近也在看ES6的一些內容,因而打算本身也整理一下,提高一下理解;
  • 本文適合一些瞭解並使用過Promise的人,若是你沒有了解或使用過Promise,建議先看一下 阮一峯 ECMAScript6 入門 之Promise

什麼是Promise

  • 異步編程的一種解決方案;
  • Promise是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果;

特色

  • 對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。
  • 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected

簡單實現

流程分析

  • 圖片來源 MDN

初始化一個Promise

  • 原始的Promise
// Promise構造函數接收一個executor函數,executor函數執行完同步或異步操做後,調用它的兩個參數resolve和reject
const promise = new Promise(function(resolve, reject) {
  /*
    若是操做成功,調用resolve並傳入value
    若是操做失敗,調用reject並傳入reason
  */
})
複製代碼
  • 包含當前的狀態
  • 當前的值
  • fulfilled的回調函數數組
  • rejected的回調函數數組
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const NewPromise = function(executor) {
  const _this = this;
  _this.status = PENDING; // 狀態
  _this.data = undefined; // 值
  _this.onResolvedCallback = []; // fulfilled的回調函數數組
  _this.onRejectedCallback = []; // rejected的回調函數數組
  // 成功
  function resolve(value) { ... }
  // 失敗
  function reject(reason) { ... }
  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}  
複製代碼

完善resolereject

  • resole是成功的調用,須要不狀態修改成fulfilled,而後當前的值設爲傳入的值,遍歷執行onResolvedCallback中的回調。
// 成功
function resolve(value) {
    只有pending狀態才能轉換爲fulfilled / rejected
    setTimeout(function() { // 異步執行全部的回調函數
        if (_this.status === PENDING) {
            _this.status = FULFILLED;
            _this.data = value;
            _this.onResolvedCallback.forEach(callback => callback(value));
        }
    });
}
複製代碼
  • reject的實現和resolve相似,只是狀態修改成rejected及執行onRejectedCallback
// 失敗
function reject(reason) {
    setTimeout(function() { // 異步執行全部的回調函數
        if (_this.status === PENDING) {
            _this.status = REJECTED;
            _this.data = reason;
            _this.onRejectedCallback.forEach(callback => callback(reason));
        }
    });
}
複製代碼
  • 如今已經實現了基本的Promise,你能夠像使用Promise同樣使用它,但尚未添加其餘的方法。
// 測試
const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11)
})
複製代碼

then方法

  • Promise對象有一個then方法,用來註冊在這個Promise狀態肯定後的回調,then方法須要寫在原型鏈上(爲何要寫在原型上不清楚的可能須要補一下JavaScript基礎了)。在Promise/A標準中,明確規定了then要返回一個新的對象,因此在咱們的實現中也返回一個新對象。
// then掛載到原型上
NewPromise.prototype.then = function(onResolved, onRejected) {
  const _this = this;
  if ( typeof onResolved !== 'function') {
   onResolved = function(value) { return value }
  }
  if ( typeof onRejected !== 'function') {
    onRejected = function(reason) { throw reason }
  }

  // 公共判斷
  const common = function (data, resolve, reject) {
    // 考慮到有可能throw,咱們將其包在try/catch塊裏
    try {
      let value = _this.status === FULFILLED
        ? onResolved(data)
        : onRejected(data)
      if( value instanceof Promise) {
        value.then(resolve, reject)
      }
      resolve(value)
    } catch (error) {
      reject(error)
    }
  }
  // 公共判斷
  const pendingCommon = function (data, flag, resolve, reject) {
    // 考慮到有可能throw,咱們將其包在try/catch塊裏
    try {
      let value = flag === FULFILLED
        ? onResolved(data)
        : onRejected(data)
      if( value instanceof Promise) {
        value.then(resolve, reject)
      }
      resolve(value)
    } catch (error) {
      reject(error)
    }
  }

  if (_this.status === PENDING) {
    return new NewPromise(function(resolve, reject) {
      _this.onResolvedCallback.push((value) => {
        pendingCommon(value, FULFILLED, resolve, reject);
      })

      _this.onRejectedCallback.push((reason) => {
        pendingCommon(reason, REJECTED, resolve, reject);
      })
    })
  } else { // resolve / reject
    return new NewPromise(function (resolve, reject) {
      setTimeout(function () {
        common(_this.data, resolve, reject)
      })
    })
  }
}
複製代碼

resolve方法再次完善

  • 判斷rosolve的值是不是Promise,若是是Promise繼續執行.then方法。
// 成功
function resolve(value) { 
    // value 若是是Promise繼續執行.then
+   if (value instanceof Promise) {
+     return value.then(resolve, reject)
+   }
    ...
}
複製代碼

catch方法

  • 捕獲Promise的異常錯誤。
// catch方法
NewPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}
複製代碼

finally方法

  • finally()方法用於指定無論Promise對象最後狀態如何,都會執行的操做。
// finally
NewPromise.prototype.finally = function (fun) {
  return this.then((value) => {
      // f(); return value;
      // Promise.resolve會等f()的函數執行完再返回結果
      return NewPromise.resolve(fun()).then(() => value);
  }, (err) => {
      return NewPromise.resolve(fun()).then(() => {
          throw err;
      });
  });
};
複製代碼

resolve方法

  • 現有對象轉爲Promise對象,實例的狀態爲fulfilled
NewPromise.resolve = function(value) {
  if (value instanceof Promise) return value;
    if (value === null) return null;
    // 判斷若是是promise
    if (typeof value === 'object' || typeof value === 'function') {
        try {
            // 判斷是否有then方法
            let then = value.then;
            if (typeof then === 'function') {
                return new NewPromise(then.call(value)); // 執行value方法
            }
        } catch (e) {
            return new NewPromise( (resolve, reject) =>{
                reject(e);
            });
        }
    }
    return new NewPromise( (resolve, reject) =>{
      resolve(value);
  });
}
複製代碼

reject方法

  • 現有對象轉爲Promise對象,實例的狀態爲rejected。實現和resolve方法相似,最後return改成reject
NewPromise.reject = function(reason) {
    ...
    return new NewPromise( (resolve, reject) =>{
      reject(reason);
  });
}
複製代碼

all方法

  • Promise.all方法用於將多個 Promise 實例,包裝成一個新的Promise 實例;方法的參數能夠不是數組,但必須具備Iterator接口,且返回的每一個成員都是Promise實例。
// all
NewPromise.all = function(promises) {
  return new NewPromise(function(resolve, reject) {
    const result = []
    // 判斷參數屬否是數組
    promises = Array.isArray(promises) ? promises : []
    const len = promises.length - 1;
    promises.forEach((promise, index) => {
      promise.then(value => {
        result.push(value)
        if(index === len) {
          resolve(result)
        }
      }, reject)
    })
  }) 
}
複製代碼

race方法

  • Promise.race()方法一樣是將多個Promise實例,包裝成一個新的Promise實例。
  • 只要參數之中有一個實例率先改變狀態,Promise.race()的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給Promise.race()的回調函數。
// race
NewPromise.race = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    promises.forEach(promise => {
      promise.then(resolve, reject)
    })
  })
}
複製代碼

deferred方法

  • 返回一個有promise方法的對象。
// defered
NewPromise.deferred = function() {
  const dfd = {}
  dfd.promise = new NewPromise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
複製代碼

allSettled方法

  • Promise.allSettled()方法接受一組Promise實例做爲參數,包裝成一個新的Promise實例,只有等到全部這些參數實例都返回結果,無論是fulfilled仍是rejected,包裝實例纔會結束。返回一個包含全部結果的數組。
NewPromise.allSettled = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    let len = promises.length;
    const argslen = len;
    if (len === 0) return resolve([]);
    let args = Array.prototype.slice.call(promises);
    function resolvePromise(index, value) {
      if(typeof value === 'object') { // 傳入的是不是object
        const then = value.then;
        if(typeof then === 'function'){
          then.call(value, function(val) {
            args[index] = { status: 'fulfilled', value: val};
            if(--len === 0) {
              resolve(args);
            }
          }, function(e) {
            args[index] = { status: 'rejected', reason: e };
            if(--len === 0) {
              reject(args);
            }
          })
        }
      }
    }
 
    for(let i = 0; i < argslen; i++){
      resolvePromise(i, args[i]);
    }
  })
}
複製代碼

測試

基礎測試

// 測試
const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11);
  resolve(123);
})
複製代碼

then測試

promise.then(val => {
  console.log('val', val)
})
複製代碼

catch測試

const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11);
  reject('errr')
})
promise.catch(err => {
  console.log('err', err)
})
複製代碼

finally測試

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
}).finally(() => {
  console.log('finally')
})
複製代碼

resolve測試

const resolved = NewPromise.resolve(1);
resolved.then(val => {
    console.log('resolved', val)
})
複製代碼

reject測試

const rejected = NewPromise.reject(-1);
rejected.catch(val => {
  console.log('rejected', val)
})
複製代碼

all測試

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
})
複製代碼

allSettled測試

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.allSettled([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
})
複製代碼

所有代碼

/*
 * @Author: detanx 
 * @Date: 2020-05-11 17:39:52 
 * @Last Modified by:   detanx 
 * @Last Modified time: 2020-05-11 17:39:52 
 */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const NewPromise = function(executor) {
  const _this = this;
  _this.status = PENDING;
  _this.data = undefined;
  _this.onResolvedCallback = [];
  _this.onRejectedCallback = [];

  // 成功
  function resolve(value) {
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    setTimeout(function() { // 異步執行全部的回調函數
      if (_this.status === PENDING) {
        _this.status = FULFILLED;
        _this.data = value;
        _this.onResolvedCallback.forEach(callback => callback(value));
      }
    })
  }
  // 失敗
  function reject(reason) {
    setTimeout(function() { // 異步執行全部的回調函數
      if (_this.status === PENDING) {
        _this.status = REJECTED;
        _this.data = reason;
        _this.onRejectedCallback.forEach(callback => callback(reason));
      }
    })
  }

  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}  
// then
NewPromise.prototype.then = function(onResolved, onRejected) {
  const _this = this;
  if ( typeof onResolved !== 'function') {
   onResolved = function(value) { return value }
  }
  if ( typeof onRejected !== 'function') {
    onRejected = function(reason) { throw reason }
  }

  // 公共判斷
  const common = function (data, resolve, reject) {
    // 考慮到有可能throw,咱們將其包在try/catch塊裏
    try {
      let value = _this.status === FULFILLED
        ? onResolved(data)
        : onRejected(data)
      if( value instanceof Promise) {
        value.then(resolve, reject)
      }
      resolve(value)
    } catch (error) {
      reject(error)
    }
  }
  // 公共判斷
  const pendingCommon = function (data, flag, resolve, reject) {
    // 考慮到有可能throw,咱們將其包在try/catch塊裏
    try {
      let value = flag === FULFILLED
        ? onResolved(data)
        : onRejected(data)
      if( value instanceof Promise) {
        value.then(resolve, reject)
      }
      resolve(value)
    } catch (error) {
      reject(error)
    }
  }

  if (_this.status === PENDING) {
    return new NewPromise(function(resolve, reject) {
      _this.onResolvedCallback.push((value) => {
        pendingCommon(value, FULFILLED, resolve, reject);
      })

      _this.onRejectedCallback.push((reason) => {
        pendingCommon(reason, REJECTED, resolve, reject);
      })
    })
  } else { // resolve / reject
    return new NewPromise(function (resolve, reject) {
      setTimeout(function () {
        common(_this.data, resolve, reject)
      })
    })
  }
}
NewPromise.resolve = function(value) {
  if (value instanceof Promise) return value;
    if (value === null) return null;
    // 判斷若是是promise
    if (typeof value === 'object' || typeof value === 'function') {
        try {
            // 判斷是否有then方法
            let then = value.then;
            if (typeof then === 'function') {
                return new NewPromise(then.call(value)); // 執行value方法
            }
        } catch (e) {
            return new NewPromise( (resolve, reject) =>{
                reject(e);
            });
        }
    }
    return new NewPromise( (resolve, reject) =>{
      resolve(value);
  });
}
NewPromise.reject = function(value) {
  if (value instanceof Promise) return value;
    if (value === null) return null;
    // 判斷若是是promise
    if (typeof value === 'object' || typeof value === 'function') {
        try {
            // 判斷是否有then方法
            let then = value.then;
            if (typeof then === 'function') {
                return new NewPromise(then.call(value)); // 執行value方法
            }
        } catch (e) {
            return new NewPromise( (resolve, reject) =>{
                reject(e);
            });
        }
    }
    return new NewPromise( (resolve, reject) =>{
      reject(value);
  });
}
// catch方法
NewPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}
// defered
NewPromise.deferred = function() {
  const dfd = {}
  dfd.promise = new NewPromise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
// all
NewPromise.all = function(promises) {
  return new NewPromise(function(resolve, reject) {
    const result = []
    promises = Array.isArray(promises) ? promises : []
    const len = promises.length - 1;
    promises.forEach((promise, index) => {
      promise.then(value => {
        result.push(value)
        if(index === len) {
          resolve(result)
        }
      }, reject)
    })
  }) 
}
// race
NewPromise.race = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    promises.forEach(promise => {
      promise.then(resolve, reject)
    })
  })
}
// allSettled
NewPromise.allSettled = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    let len = promises.length;
    const argslen = len;
    if (len === 0) return resolve([]);
    let args = Array.prototype.slice.call(promises);
    function resolvePromise(index, value) {
      if(typeof value === 'object') {
        const then = value.then;
        if(typeof then === 'function'){
          then.call(value, function(val) {
            args[index] = { status: 'fulfilled', value: val};
            if(--len === 0) {
              resolve(args);
            }
          }, function(e) {
            args[index] = { status: 'rejected', reason: e };
            if(--len === 0) {
              reject(args);
            }
          })
        }
      }
    }
 
    for(let i = 0; i < argslen; i++){
      resolvePromise(i, args[i]);
    }
  })
}

// 測試
// const promise = new NewPromise(function(resolve, reject) {
//   console.log('ss', 11)
//   // resolve(123)
//   reject('errr')
//   // throw 'ree'
// })
// promise.catch(val => {
//   console.log('val', val)
// })
// const promise1 = new NewPromise(function(resolve, reject) {
//   console.log('ss', 11)
//   resolve(123)
//   // reject('errr')
// })
// const rejected = NewPromise.reject(-1);
// rejected.catch(val => {
//     console.log('rejected', val)
//   })
// console.log('resolved', resolved)
const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
})
複製代碼

參考

阮一峯 ECMAScript 6 入門
22 道高頻 JavaScript 手寫面試題及答案es6

  • 有幫到您的話留下你的贊👍唄,你的支持就是我產出的動力。
相關文章
相關標籤/搜索