昨天看了美团技术博客的一篇讲promise的文章,里面实现了一个简单的promise,在这里巩固并修改。
promise的使用是这样的:
let promise_1 = new myPromise((resolve, reject) => {
setTimeout(() => { resolve(100); }, 1000);
});
promise_1.then(rst => {
console.log( rst);
});
复制代码
promise大家平时经常用到,下面我们尝试实现一个简单的promise。
为了方便,假设promise_1是一个promise实例。
构造函数:
function myPromise(fn) {
let status = 'pending';//promise对象的状态
let val;//promise对象的值
}
复制代码
首先,promise对象在实例化时要求必须传入一个函数。
function required(name){
throw new Error('param '+name+' is required')
}
function myPromise(fn = required('fn')) {
let status = 'pending';//promise对象的状态
let val;//promise对象的值
fn();
}
复制代码
required函数的作用是在用户没传入参数时会报错。
然后,promise对象在实例化时会同步的执行传入的函数(fn)。
promise的回掉函数要通过promise_1.then()来注册,并且promise_1.then()返回的也应该是一个promise,这样才能实现链式调用。
修改一下代码:
function myPromise(fn = required('fn')) {
let status = 'pending';//promise对象的状态
let val;//promise对象的值
this.then = (onResolve,onReject)=>{
return new myPromise((resolve, reject)=>{
//我是promise_return
});
}
fn();
}
复制代码
现在我们的promise_1有了then,then()也返回一个promise(我们叫他promise_return)。但是这两个promise并没有什么直接关系。
我们现在尝试实现这样的效果:
也就是说我们需要一个数组,来存放promise实例的多个回调。
function myPromise(fn = required('fn')) {
let next_promiseInfos = [];//当前promise对像的回调们
let status = 'pending';//promise对象的状态
let val;//promise对象的值
this.then = (onResolve,onReject)=>{
return new myPromise((resolve, reject)=>{
//我是promise_return
});
}
fn();
}
复制代码
现在我们想想如何将当前promise和他的多个回调相关联。
首先,如果当前promise_1对象状态为pending,此时通过promise_1.then(onResolve)给它添加一个回调,这个onResolve不会立刻执行,而是等待promise_1对象调用resolve并且自身状态变为resolved后,才会执行。我们实现一个handle方法,它会将promise对象的每个回调的相关信息存在数组中。
function myPromise(fn = required('fn')) {
let next_promiseInfos = [];//当前promise对像的回调们
let status = 'pending';//promise对象的状态
let val;//promise对象的值
this.then = (onResolve,onReject)=>{
return new myPromise((resolve, reject)=>{
//我是promise_return
handle({
resolve, reject, onResolve, onReject
});
});
}
function handle(promise_info) {
if (status === 'pending') {
next_promiseInfos.push(promise_info);
return;
}
}
fn();
}
复制代码
我们可以看到,将onResolve和resolve存入promise_1的next_promiseInfos数组中(关于reject的先不讨论)。
现在我们的promise_1有了各个回调的相关信息(onResolve和每个promise_return的resolve)。我们想一想在promise_1调用resolve时应该发生什么。
promise_1调用resolve时,promise_1的值(代码中的 val)会变成传入resolve函数的值,peomise_1的状态变为resolved,最后,执行promise_1的next_promiseInfos数组中每个元素的onResolve回调,并且为了满足链式调用,需要将回调的执行结果传给下一个promise
我们实现resolve函数:
function resolve(value) {
val = value;
status = 'resolved';
doCallbacks();
}
function doCallbacks() {
setTimeout(() => {
next_promiseInfos.forEach(promise_info => {
handle(promise_info);
});
}, 0);
}
复制代码
对handle函数稍作修改:
function handle(promise_info) {
if (status === 'pending') {
next_promiseInfos.push(promise_info);
return;
}
rst = promise_info.onResolve(val);//执行回调并返回结果
promise_info.resolve(rst);//将返回的结果传入下一个promise
}
复制代码
大家对最后一行代码可能不明白,他的作用就是将回调执行的结果传给promise_1.then()生成的新promise,这样就可以实现链式调用。
我们先测试一下现在的代码:
function required(name) {
throw new Error('param ' + name + ' is required')
}
function myPromise(fn = required('fn')) {
let next_promiseInfos = [];//当前promise对像的回调们
let status = 'pending';//promise对象的状态
let val;//promise对象的值
this.then = (onResolve, onReject) => {
return new myPromise((resolve, reject) => {
//我是promise_return
handle({
resolve, reject, onResolve, onReject
});
});
}
function resolve(value) {
val = value;
status = 'resolved';
doCallbacks();
}
function doCallbacks() {
setTimeout(() => {
next_promiseInfos.forEach(promise_info => {
handle(promise_info);
});
}, 0);
}
function handle(promise_info) {
if (status === 'pending') {
next_promiseInfos.push(promise_info);
return;
}
rst = promise_info.onResolve(val);//执行回调并返回结果
promise_info.resolve(rst);//将返回的结果传入下一个promise
}
fn(resolve);
}
复制代码
测试代码:
console.log('程序执行')
let promise_1 = new myPromise((resolve, reject) => {
setTimeout(() => { resolve(100); }, 1000);
});
promise_1.then(rst => {
console.log(1, rst);
});
promise_1.then(rst => {
console.log(2, rst);
});
promise_1.then(rst => {
console.log(3, rst);
});
promise_1.then(rst => {
console.log(4, rst);
return rst +11;
}).then(rst=>{
console.log('链式调用',rst)
return rst+2;
}).then(rst=>{
console.log('链式调用2',rst)
});
复制代码
执行结果:
可以看到,程序按照我们预想的方式执行了。
大家使用promise时可能会在xxx.then()注册的回调中返回一个promise对象,他的执行效果是这样的(注意输出的时间):
在程序执行1秒后,输出1 100,然后该回调返回一个promise,这个promise两秒后resolve(100+2),最后一个回调的实参就是100+2。
我们如何实现这个回调中返回promise的效果呢?
目前,我们实现的promise在resolve后,直接就将状态改为resolve,并且执行后面的回调。这种方式肯定是不行的了。我们在resolve时应该判断传入参数的类型,如果是promise类型,那么我们就等待这个promise执行完成之后再执行resolve。这样才能满足上面说的效果。 我们修改一下resolve的代码:
function resolve(value) {
//判断一下value的类型
if (typeof value === 'object' && value.then && typeof value.then === 'function') {
//如果看起来像一个promise,我们就当他是promise
value.then(resolve);
return;
}
val = value;
status = 'resolved';
doCallbacks();
}
复制代码
如果传入promise_1的resolve函数的value也是一个promise(暂时叫他value_promise),那么我们给他注册一个回调。并且,回调函数就是resolve。
如果value_promise执行完成,他的回调函数会被执行,并将value_promise的值传给回调函数(promise_1的resolve)。也就是说,在value_promise调用他自己的resolve(xxxValue)后,会调用promise_1的resolve(xxxValue)。
这样,promise_1就得到了value_promise的值。
现在的代码:
function required(name) {
throw new Error('param ' + name + ' is required')
}
function myPromise(fn = required('fn')) {
let next_promiseInfos = [];//当前promise对像的回调们
let status = 'pending';//promise对象的状态
let val;//promise对象的值
this.then = (onResolve, onReject) => {
return new myPromise((resolve, reject) => {
//我是promise_return
handle({
resolve, reject, onResolve, onReject
});
});
}
function resolve(value) {
//判断一下value的类型
if (typeof value === 'object' && value.then && typeof value.then === 'function') {
//如果看起来像一个promise,我们就当他是promise
value.then(resolve);
return;
}
val = value;
status = 'resolved';
doCallbacks();
}
function doCallbacks() {
setTimeout(() => {
next_promiseInfos.forEach(promise_info => {
handle(promise_info);
});
}, 0);
}
function handle(promise_info) {
if (status === 'pending') {
next_promiseInfos.push(promise_info);
return;
}
rst = promise_info.onResolve(val);//执行回调并返回结果
promise_info.resolve(rst);//将返回的结果传入下一个promise
}
fn(resolve);
}
复制代码
测试代码:
console.log('程序执行')
let promise_1 = new myPromise((resolve, reject) => {
setTimeout(() => { resolve(100); }, 1000);
});
promise_1.then(rst => {
console.log('aaa', rst);
return new myPromise((resolve)=>{
setTimeout(()=>{
resolve(rst+1)
},1000)
})
}).then(rst => {
console.log('bbb', rst);
return new myPromise((resolve)=>{
setTimeout(()=>{
resolve(rst+1)
},1000)
})
}).then(rst => {
console.log('ccc', rst);
return new myPromise((resolve)=>{
setTimeout(()=>{
resolve(rst+1)
},1000)
})
}).then(rst=>{
console.log('finally',rst)
});
复制代码
执行结果:
可以看到程序执行正确,间隔1秒输出结果。
目前,我们已经实现了可以resolve的promise。应该已经理解了实现一个promise的原理。
catch和reject的实现并不复杂,如果有需要会再写一个文章介绍如何实现。
完整的代码:
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>promise</title>
</head>
<body>
<script>
function required(name){
throw new Error('param '+name+' is required')
}
function myPromise(fn = required('fn')) {
let next_promiseInfos = [];
let status = 'pending';
let val;
let catcher = null;
this.then = (onResolve, onReject) => {
return new myPromise((resolve, reject) => {
handle({
resolve, reject, onResolve, onReject
});
});
}
this.catch = (cb) => {
catcher = cb;
}
function resolve(value) {
if (typeof value === 'object' && value.then && typeof value.then === 'function') {
value.then(resolve,reject);
return;
}
val = value;
status = 'resolved';
doCallbacks();
}
function doCallbacks() {
setTimeout(() => {
next_promiseInfos.forEach(promise_info => {
handle(promise_info);
});
}, 0);
}
function reject(err) {
status = 'reject';
val = err;
if (catcher) {
catcher(err);
return;
}
if (!catcher && next_promiseInfos.length < 1) {
throw new Error('uncaught reject:' + err);
}
doCallbacks();
}
function handle(promise_info) {
if (status === 'pending') {
next_promiseInfos.push(promise_info);
return;
}
if (status === 'reject') {
let cb = promise_info.onReject ? promise_info.onReject : promise_info.reject;
cb(val);
return;
}
let rst;
try{
rst = promise_info.onResolve(val);
}catch(err){
promise_info.reject(err);
return;
}
promise_info.resolve(rst);
}
fn(resolve, reject);
}
console.log('程序执行')
let promise_1 = new myPromise((resolve, reject) => {
setTimeout(() => { resolve(100); }, 1000);
});
promise_1.then(rst => {
console.log(1,rst);
return new myPromise((resolve,reject)=>{
setTimeout(()=>{
resolve(rst +2);
},2000);
});
}).then(rst=>{
console.log('wait',rst);
throw new Error('出错了');
}).then(rst=>{
console.log(rst);
}).catch(err=>{
console.log('catch 到错误了',err)
});
</script>
</body>
</html>
复制代码
参考:https://tech.meituan.com/promise-insight.html
我的 github
我的 博客