第22题 手写Promise最简20行版本,实现异步链式调用
实现代码
function Promise(fn) {
this.cbs = [];
const resolve = (value) => {
setTimeout(() => {
this.data = value;
this.cbs.forEach((cb) => cb(value));
});
}
fn(resolve.bind(this));
}
Promise.prototype.then = function (onResolved) {
return new Promise((resolve) => {
this.cbs.push(() => {
const res = onResolved(this.data);
if (res instanceof Promise) {
res.then(resolve);
} else {
resolve(res);
}
});
});
};
核心案例
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
})
.then(console.log);
本文将围绕这个最核心的案例来讲,这段代码的表现如下:
- 500ms 后输出 1
- 500ms 后输出 2
构造函数
首先来实现 Promise 构造函数
function Promise(fn) {
// Promise resolve时的回调函数集
this.cbs = [];
// 传递给Promise处理函数的resolve
// 这里直接往实例上挂个data
// 然后把onResolvedCallback数组里的函数依次执行一遍就可以
const resolve = (value) => {
// 注意promise的then函数需要异步执行
setTimeout(() => {
this.data = value;
this.cbs.forEach((cb) => cb(value));
});
}
// 执行用户传入的函数
// 并且把resolve方法交给用户执行
fn(resolve.bind(this));
}
好,写到这里先回过头来看案例
const fn = (resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
};
new Promise(fn);
分开来看,fn 就是用户传的函数,这个函数内部调用了
resolve函数后,就会把promise实例上的 cbs 全部执行一遍。
到此为止我们还不知道 cbs 这个数组里的函数是从哪里来的,接着往下看。
then
这里是最重要的 then 实现,链式调用全靠它:
Promise.prototype.then = function (onResolved) {
// 这里叫做promise2
return new Promise((resolve) => {
this.cbs.push(() => {
const res = onResolved(this.data);
if (res instanceof Promise) {
// resolve的权力被交给了user promise
res.then(resolve);
} else {
// 如果是普通值 就直接resolve
// 依次执行cbs里的函数 并且把值传递给cbs
resolve(res);
}
});
});
};
再回到案例里
const fn = (resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
};
const promise1 = new Promise(fn);
promise1.then((res) => {
console.log(res);
// user promise
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
});
注意这里的命名 :
- 我们把
new Promise返回的实例叫做promise1 - 在
Promise.prototype.then的实现中,我们构造了一个新的 promise 返回,叫它promise2 - 在用户调用 then 方法的时候,用户手动构造了一个
promise并且返回,用来做异步的操作,叫它user promise - 那么在
then的实现中,内部的this其实就指向promise1 - 而
promise2的传入的fn函数执行了一个this.cbs.push(),其实是往promise1的cbs数组中push了一个函数,等待后续执行
Promise.prototype.then = function (onResolved) {
// 这里叫做promise2
return new Promise((resolve) => {
// 这里的this其实是promise1
this.cbs.push(() => {});
});
};
那么重点看这个
push的函数,注意,这个函数在promise1被resolve了以后才会执行。
// promise2
return new Promise((resolve) => {
this.cbs.push(() => {
// onResolved就对应then传入的函数
const res = onResolved(this.data)
// 例子中的情况 用户自己返回了一个user promise
if (res instanceof Promise) {
// user promise的情况
// 用户会自己决定何时resolve promise2
// 只有promise2被resolve以后
// then下面的链式调用函数才会继续执行
res.then(resolve)
} else {
resolve(res)
}
})
})
如果用户传入给 then 的
onResolved方法返回的是个user promise,那么这个user promise里用户会自己去在合适的时机resolve promise2,那么进而这里的res.then(resolve)中的resolve就会被执行:
if (res instanceof Promise) {
res.then(resolve)
}
结合下面这个例子来看:
new Promise((resolve) => {
setTimeout(() => {
// resolve1
resolve(1);
}, 500);
})
// then1
.then((res) => {
console.log(res);
// user promise
return new Promise((resolve) => {
setTimeout(() => {
// resolve2
resolve(2);
}, 500);
});
})
// then2
.then(console.log);
then1这一整块其实返回的是promise2,那么then2其实本质上是promise2.then(console.log),- 也就是说
then2注册的回调函数,其实进入了promise2的 cbs 回调数组里,又因为我们刚刚知道,resolve2调用了之后,user promise会被resolve,进而触发promise2被resolve,进而promise2里的cbs数组被依次触发 - 这样就实现了用户自己写的
resolve2执行完毕后,then2里的逻辑才会继续执行,也就是异步链式调用
简单实现一个可以异步链式调用的
promise,而真正的promise比它复杂很多很多,涉及到各种异常情况、边界情况的处理。
promise A+规范还是值得每一个合格的前端开发去阅读的
其他版本实现
简易版的Promise:
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise (fn) {
let that = this;
that.status = PENDING;
that.value = null;
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
function resolve (value) {
if (that.status === PENDING) {
that.status = RESOLVED;
that.value = value;
that.resolvedCallbacks.forEach(cb => cb(value))
}
}
function reject (value) {
if (that.status === PENDING) {
that.status = REJECTED;
that.value = value;
that.rejectedCallbacks.forEach(cb => cb(value))
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
let that = this;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r };
if (that.status === PENDING) {
that.resolvedCallbacks.push(onFulfilled);
that.rejectedCallbacks.push(onRejected);
}
if (that.status === RESOLVED) {
onFulfilled(that.value)
}
if (that.status === REJECTED) {
onRejected(that.value)
}
}
new MyPromise((resolve, reject) => {
console.log('我立即执行')
setTimeout(() => {
resolve(1)
}, 1000)
}).then(res => {
console.log(res)
})
