Promise成爲你們解決異步回調地獄問題的重要方法,理解它的原理和底層實現有助於咱們更加合理的使用。如下遵循PromiseA+規範實現了一個簡單的Promise.jquery
實現分爲六步:數組
明確promise有三個狀態: pending fulfilled rejectedpromise
在new Promise時, excutor會當即執行bash
每一個promise上會有一個then方法, then方法中傳遞兩個參數onFulfilled和onRejected異步
代碼以下:測試
class Promise{
constructor(excutor){
this.status = 'pending'; // 默認是等待態
this.value = undefined;
this.reason = undefined;
let resolve = (value) => { // 調用resolve, 變成成功態
if (this.status === 'pending') {
this.status= 'fulfilled';
this.value = value;
}
};
let rejected = (reason) => { // 調用reject, 變成失敗態
if (this.status === 'pending') {
this.status= 'rejected';
this.reason = reason;
}
};
excutor(resolve, rejected);
}
then(onFulfilled, onRejected){
console.log(this.status, 'status');
if (this.status === 'fulfilled') { // 變成成功態fulfilled時執行成功回調
onFulfilled(this.value);
}
if (this.status === 'rejected') { // 變成失敗態rejected時執行失敗回調
onFulfilled(this.reason);
}
}
}
複製代碼
核心思路: 將成功和失敗的回調放在數組中, 當狀態改變時, 執行對應狀態的事件ui
class Promise{
constructor(excutor){
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCbs = []; // 存放成功狀態的回調
this.onRejectedCbs = []; // 存放失敗狀態的回調
let resolve = (value) => {
if (this.status === 'pending') {
this.status= 'fulfilled';
this.value = value;
this.onFulfilledCbs.forEach(cb => cb()); // 依次執行成功回調
}
};
let rejected = (reason) => {
if (this.status === 'pending') {
this.status= 'rejected';
this.reason = reason;
this.onRejectedCbs.forEach(cb => cb()); // 依次執行失敗回調
}
};
excutor(resolve, rejected)
}
then(onFulfilled, onRejected){
...
if (this.status === 'pending') { // 有異步邏輯時,狀態爲pending 將callback放在數組中
this.onFulfilledCbs.push(() => {
onFulfilled(this.value);
});
this.onRejectedCbs.push(() => {
onRejected(this.reason);
});
}
}
}
複製代碼
思路: 返回一個新的promise實例. jquery中的方法也能鏈式調用, 是經過return this實現的.可是promise不能返回this, 由於, 一個promise的狀態若是是fulfilled,就不能變成rejected.this
then的特色:spa
class Promise{
constructor(excutor){
...
try{ // 1. 在執行excutor時有可能拋出錯誤 直接捕獲錯誤
excutor(resolve, reject);
}catch(err) {
reject(err);
}
}
// resolvePromise 判斷當前promise返回結果x和promise2的關係
resolvePromise(promise2, x, resolve, reject) {
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
try{ // 取then方法時 有可能報錯
let then = x.then;
if (typeof then === 'function') { // 2.2.2 返回一個promise 執行promise
then.call(x, (y) => {
resolve(y);
}, (r) => {
reject(r);
});
} else { // 2.2.3 返回一個帶有then屬性的普通對象 {then: ...}
resolve(x);
}
}catch(e){
reject(e);
}
} else {// 2.2.1 返回一個常量
resolve(x);
}
}
then(onFulfilled, onRejected){
// 2.實現then的鏈式調用(jq)
let promise2 = new Promise((resolve, reject) => { // 2.1 返回一個新的promise
if (this.status === 'fulfilled') {
try{ // 若是方法執行報錯 直接拋出錯誤
let x = onFulfilled(this.value); // 2.2 處理前一個promise中onFulfilled的不一樣返回結果和下一個promise的關係: 普通值/promise
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}
if (this.status === 'rejected') {
try{
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}
if (this.status === 'pending') {
this.onFulfilledCbs.push(() => {
try{
let x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
});
this.onRejectedCbs.push(() => {
try{
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
});
}
});
return promise2;
}
}
複製代碼
class Promise{
...
resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 4.1 return的值不能爲promise2(本身等待本身) 不然會報循環引用錯誤
return reject(new Error('循環引用'));
}
let called; // 4.4 定義called 避免成功失敗只能調用一個
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
try{
let then = x.then;
if (typeof then === 'function') {
then.call(x, (y) => {
if (called) return; // 成功和失敗只會掉一個
called = true;
this.resolvePromise(promise2, y, resolve, reject); // 4.3 若是y是一個promise 進行遞歸解析
}, (r) => {
if (called) return; // 若是掉過成功
called = true;
reject(r);
});
} else {
if (called) return;
called = true;
resolve(x);
}
}catch(e){
if (called) return;
called = true;
reject(e);
}
} else {
if (called) return;
called = true;
resolve(x);
}
}
then(onFulfilled, onRejected){
let promise2 = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => { // 4.2 若是不加setTimeout會致使promise2爲undefined-->then方法都是異步的
try{
let x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject); // 初始化promise2未完成, 爲undefined
}catch(e){
reject(e);
}
});
}
if (this.status === 'rejected') {
setTimeout(() => {
try{
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
});
}
....
});
return promise2;
}
}
module.exports = Promise;
複製代碼
思路: 若是promise執行resolve, 就將resolve中的值經過onFULfilled傳遞; 若是promise執行reject, 就將reject中的值經過onRejected傳遞插件
then(onFulfilled, onRejected){
// 5 穿透實現
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (data) => {
return data;
};
onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
throw err;
};
...
}
複製代碼
在promise A+規範中提出, 經過promises-aplus-tests插件對本身寫的promise進行檢驗, 可是這個插件使用前須要經過promise.defer將promise的屬性掛載在defer上, 代碼以下:
Promise.deferred = Promise.defer = () => {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
複製代碼
下面就能夠經過promises-aplus-tests插件對本身寫的promise進行檢驗了: