源码地址:
https://github.com/qyzhizi/js-logical/blob/main/2024-09-30-promise-demo1/my_promise_20lines.js
代码解释
function Promise(fn) {
this.cbs = [];
const resolve = (value) => {
setTimeout(() => {
this.data = value;
this.cbs.forEach((cb) => cb(value));
});
}
fn(resolve);
}
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);
}
});
});
};
// test
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
})
.then(console.log);
resolve 闭包
const resolve = (value) => {
setTimeout(() => {
this.data = value;
this.cbs.forEach((cb) => cb(value));
});
}
this 表示构造函数生成的 Promise 实例的地址,这个函数可以在其他 Promise 实例中被调用,比如在另一个 Promise 实例的 cbs 中等待调用。
setTimeout 是一个异步函数,会被提交到事件循环中。而 then 函数的作用是将一个函数放入到 该造函数生成的 Promise 实例的 this.cbs 中,此时事件循环还未执行提交的函数, 也就是说 then 函数 对 this.cbs 的操作早就执行了,就等事件循环执行了。
回到 resolve 函数,当事件循环开始执行 setTimeout 中的函数时,会设置 this.data 的值,然后执行 then 注册的函数。
resolve 函数的执行 在函数fn(resolve);完成,函数fn(resolve)的调用是一早就在构造函数中完成的,而函数fn是在new Promise(fn)` 时传递的,用户自定义的函数,用户必须在 fn 中调用 resolve, 比如发送完一个请求后,调用 resolve(res) , res 表示请求的结果。
在这段代码中,setTimeout 函数用于模拟异步操作,但是在代码中并没有明确给出具体的延迟时间参数。比如像 setTimeout(() => , 500) 这样明确写了 500 毫秒的就是指定了延迟时间,而如果像 setTimeout(() => ) 这样没有给出具体数字的参数,就意味着延迟时间没有明确指定,具体的延迟时间将由浏览器或运行环境来决定,通常会有一个默认的时间,但这个默认时间不是固定的,可能会因不同的环境而有所差异。
Promise 原型中的then 函数
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);
}
});
});
};
如上面所说的 then 函数是为了注册新函数到 调用者(promise 实例)的 cbs 列表中,方便在 调用者执行 resolve 时被调用,实现了链式调用重要的一环。 onResolved 也是一个函数,它的作用是对 this.data (调用者.data)的后续处理, 这里的 this.data 需要注意,当调用者(promise 实例)执行 than 函数的时候,此时 this 被绑定为 调用者(promise 实例)的地址, 因为此时 new Promise 所传入的函数 才刚刚被定义,在构造函数执行时还没有被定义,那时应该只是一串代码。
this.cbs.push
的函数 是个箭头函数,先执行 const res = onResolved(this.data);
关键是后面的处理过程,如果 res 是 Promise 实例,那么就执行 res.then(resolve);
否者执行 resolve(res); 这里是个难点不好理解。首先 res 实例 是一个 user promise, 不在主线 promise 上面, res.then(resolve);
表示在另一个 promise 中注册这个 resolve 函数,注意这里的 resolve 是一个主线上即将返回的 promise 的闭包,可以让主线 promise 调用链继续执行的关键,而注册到 另一个 promise 中 cbs 中,是为了等待 另一个 promise 执行完毕后,在继续执行主线上的 resolve。
如果 res 不是 Promise 实例,那么就直接执行 resolve(res); 继续执行主链上的操作。
调用测试
// test
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
})
.then(console.log);
结果:
-> % node test.js
1
2
主线 promise 起始于 new Promise, 在第一个 then 函数中 返回了一个 user promise, 第二个 then 注册的函数会在 user promise 完成后再执行
参考:
https://juejin.cn/post/6844904094079926286
https://leetcode.cn/circle/discuss/QTeFbj/