function MyPromise(fn) {
// 缓存当前的this,方便引用
let promise = this;
// 初始化当前实例的value,error,处理成功事件的arr,处理失败事件的arr
// 初始化当前状态,默认为pending
promise.value = null;
promise.error = null;
promise.successArr = [];
promise.errorArr = [];
promise.status = 'pending';
// 必须通过new来调用
if (this instanceof MyPromise === false) {
return new MyPromise(fn)
}
// 定义当前的resolve方法
const resolve = function (val) {
// 确保在then方法执行之后再执行resolve
setTimeout(function () {
promise.value = val;
// 判断是否已经改变了状态
if (promise.status !== "pending") {
console.log("状态已经为error,不能再更新了");
return
}
promise.status = "success";
promise.successArr.forEach(function (callback) {
promise.value = callback(promise.value)
})
}, 0)
};
const reject = function (err) {
// 确保在then方法执行之后再执行reject
setTimeout(function () {
promise.error = err;
if (promise.status !== "pending") {
console.log("状态已经为success,不能再更新了");
return
}
promise.status = "error";
promise.errorArr.forEach(function (callback) {
promise.error = callback(promise.error)
})
}, 0)
};
// 捕获执行过程中的错误,并reject出去
try {
// 将resolve跟reject方法传进回调函数
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
// 定义原型上的方法
MyPromise.prototype = {
then: function (successEvent, errorEvent) {
// 缓存当前this
let promise = this;
return new MyPromise(function (resolve, reject) {
// 包装then中的成功回调函数
// 为什么要包装?因为可以处理返回值,
// 并且在得到返回值后再进行resolve,从而保证下一个then方法按顺序执行
const successHandler = function (value) {
// 将当前的函数运行结果传给ret
let ret = typeof successEvent === "function" && successEvent(value);
// 如果返回的是一个promise对象,则执行then方法,并将返回的value值,resolve到当前promise
if (!!ret && typeof ret['then'] === "function") {
ret.then(function (value) {
resolve(value)
}, function (error) {
reject(error)
})
} else {
resolve(ret)
}
};
// 包装then中的失败回调函数
const errorHandler = function (error) {
if( typeof errorEvent === "function"){
let ret = errorEvent(error);
resolve(ret);
}else{
reject(error)
}
};
// 判断上一级promise的状态
if (promise.status === "pending") {
// 将成功的处理函数,失败的处理函数分别推进数组里
promise.successArr.push(successHandler);
promise.errorArr.push(errorHandler)
} else if (promise.status === "success") {
successHandler(promise.value)
} else if (promise.status === "error") {
errorHandler(promise.error)
}
})
},
// 捕获错误
catch: function (errorEvent) {
return this.then(function (value) {
return value
}, errorEvent)
},
// 当全部的promise成功时,返回一个包含所有结果的数组,当有一个失败时,则返回错误的原因
all: function (arr) {
if (!(arr instanceof Array) || arr.length === 0) {
console.error("当前传入的需要数组而且不是空数组");
return
}
return new MyPromise(function (resolve, reject) {
let length = arr.length,
result = [];
arr.forEach(function (promise, index) {
promise.then(function (value) {
result.push(value);
if (index === length - 1) {
resolve(result)
}
}, function (error) {
console.log("该死,某一项出错了");
reject(error)
})
})
})
},
// 有一个promise变化,则变化
race: function (arr) {
if (!(arr instanceof Array) || arr.length === 0) {
console.error("当前传入的必须为数组而且不能是空数组");
return
}
return new MyPromise(function (resolve, reject) {
arr.forEach(function (promise) {
promise.then(function (value) {
resolve(value)
}, function (err) {
reject(err)
})
})
})
},
// 延迟函数
delay: function (value, ms) {
return this.then(function (val) {
return new MyPromise(function (resolve, reject) {
setTimeout(function () {
resolve(value || val)
}, ms)
})
})
}
};
// 改变当前的constructor指向,并设为不可枚举
Object.defineProperty(MyPromise.prototype, "constructor", {
writable: true,
enumerable: false,
configurable: true,
value: MyPromise
});
// 快速新建一个Promise对象
MyPromise.resolve = function (arg) {
if (typeof arg === "object" && typeof arg['then'] === "function") {
return arg
} else {
return new MyPromise(function (resolve, reject) {
resolve(arg)
})
}
};
MyPromise.reject = function (arg) {
return new MyPromise(function (resolve, reject) {
reject(arg)
})
};
// 测试内容
new MyPromise(function (resolve, reject) {
setTimeout(function () {
resolve("正确");
}, 1000);
}).then(function (res) {
console.log(res);
return MyPromise.resolve(333)
}).then(function (res) {
console.log(res);
// return MyPromise.reject("出错了!")
}).then(function (res) {
console.log(res);
return MyPromise.reject("出错了")
}).catch(function (err) {
console.log(err);
return 11
}).then(function (res) {
console.log(res);
}).then(function (e) {
console.log(e);
return "test"
}).catch(function (e) {
console.log(e);
return "555"
}).then(function (res) {
console.log(res);
return MyPromise.reject("大猪头")
}).then(function (res) {
}).then(function () {
},function (err) {
setTimeout(function () {
console.log(err);
},1000)
})
思路是这样的:
第一,实例化promise A需要接受一个回调函数,我们在初始化时,缓存当前的this,然后定义成功回调函数数组,失败回调函数数组等,最后将定义好的resolve与reject传入回调函数(此处要注意利用setTimeOut,将resolve函数延后执行),回调函数要保持状态单一性,一旦pending改变,就无法再更改状态;
第二,在原型上实现then方法:then方法是最难理解的:首先,为了链式调用,需要返回一个新的promise实例B;其次,为了在执行完then中的回调函数后,能将返回值传给下一个promise实例C,需要将then方法传入的两个回调函数重新包装成新的函数;接下来,就得判断上一级promiseA的状态了,如果是“pending”,就将包装好的函数分别存在上一级数组里,但如果是“success”或者“error”,证明上一级的状态已经被resolve或者reject了,就传入当前实例上的value或error属性(具体看你成功还是失败)到对应的包装函数上,并执行;举个例子,假设promiseA在延迟3秒后执行resolve的方法,就会将当前的会函数数组的回调函数取出来执行,执行完后,回调函数内部调用了promiseB的resolve,如果执行了then方法,就将状态传给promiseC;then方法的细节有很多,希望能仔细研究;
第三,catch方法:catch方法只需要调用this.then就可以了;catch方法在没有捕获到错误时,只会将上一级传递的参数传递到下一级,如果捕获到,则下一级then也能正常运行并接受catch方法的返回值;则需要注意一点是,不能直接在第一个参数传undefined,目前还没有优化;
第四,Promise.all跟Promise.race方法,跟原生差不多,按照功能实现就可以了;
第五,Promise.resolve,Promise.reject,快速生成一个Promise对象,直接调用then即可用;
就这么多了,爆肝研究原理并且处理各种bug,终于跟原生的差不多了,还有一个小细节,就是Promise对吞并错误,但是我还没实现,你不知道的JavaScript的作者提到,promise吞并错误往往会造成严重的调试时的困难,所以就不进行处理了;
参考:手把手教你实现一个完整的 Promise 原文有少少瑕疵,但是足够详细,大家可以去参考下~