Promise 链式调用原理-精简20行代码
前言
在面试的过程中,总有一些面试官会问你,手写一个简易版的Promise
得行不,得行的话就写一个出来看看,啪一哈,就把纸和笔给了你。
我们思索半天就写出来了个下面这个。
哦豁,高薪张开了它的翅膀,远离了我们。
class Promise {
constructor (resolve, reject) {}
resolve () {}
reject (){}
then () {}
catch () {}
once () {}
all () {}
...
}
本篇文章将不讲述手写出来一个简易的Promise
,感兴趣的朋友可以去看我这篇文章 -> Promise详解-手写Promise,实现一款自己的简易Promise
本篇文章记录的是如何实现Promise
的核心功能之一的.then 链式调用
,采用构造函数
的写法,本篇文章的代码不考虑任何容错和异常处理,只单独说明其链式调用
原理,方便理解。
先摆上完整代码,去掉注释和一些换行共20行有余。
代码
function CustomPromise (fn) {
// 回调收集
this.callbackList = []
// 传递给Promise处理函数的resolve
const resolve = (value) => {
// 注意promise的then函数需要异步执行
setTimeout(() => {
// 这里直接往实例上挂个data
this.data = value;
// 把callbackList数组里的函数依次执行一遍
this.callbackList.forEach(cb => cb(value))
});
}
/*
执行用户传入的函数
并且把resolve方法交给用户执行
*/
fn(resolve)
}
/*
重点
*/
// 往构造函数的原型上挂载.then方法
CustomPromise.prototype.then = function (onReaolved) {
// return 一个promise 实例
return new CustomPromise((resolve) => {
// 往回调数组中插入回调
this.callbackList.push(()=>{
const response = onReaolved(this.data)
// 判断是否是一个 CustomPromise
if(response instanceof CustomPromise){
// resolve 的权力被交给了user promise
response.then(resolve)
}else{
// 如果是普通值,直接resolve
// 依次执行callbackList里的函数 并且把值传递给callbackList
resolve(response)
}
})
})
}
经典案例
new CustomPromise((resolve) => {
setTimeout(() => {
// resolve1
resolve(1);
}, 300);
}).then((res) => {// then1
console.log(res);
// 返回一个 CustomPromise
return new CustomPromise((resolve) => {
setTimeout(() => {
// resolve2
resolve(2);
}, 300);
});
}).then(res => {// then2
console.log(res);
});
完整的代码和例子已奉上,现在来进行解释。 固然结果很重要,但过程也很重要。我们要做到 知其然知其所以然。
解析
第一步
首先,我们我们先创建这样一个Promise
, 这里需要使用匿名函数
,不能使用箭头函数
,或者你可以根据这个方法已class
类的方法进行实现。
大概步骤如下:
- 声明构造函数/类
- 在内部声明一个数组名为
callbackList
用来装回调,并放到this
里面 - 声明一个名
resolve
的方法,用来传递给Promise
进行处理,注意:resolve 内部需要为异步,这里可以采用 setTimeout 实现
- 循环
callbackList
并执行里面的方法
写出来后的样子长这样:
function CustomPromise (fn) {
// 回调收集
this.callbackList = []
// 传递给Promise处理函数的resolve
const resolve = (value) => {
// 注意promise的then函数需要异步执行
setTimeout(() => {
// 这里直接往实例上挂个data
this.data = value;
// 把callbackList数组里的函数依次执行一遍
this.callbackList.forEach(cb => cb(value))
});
}
/*
- fn 为用户传进来的函数
- 执行用户传入的函数
- 并且把resolve方法交给用户执行
*/
fn(resolve)
}
第二步
注意:第二步是本篇文章的重点,也是这个核心功能的一个重点。
我们需要往CustomPromise
的原型上挂载一个.then
的方法。并返回的是一个Promise
实例,这里依旧使用的是匿名函数
。
完整代码长这样:
// 往构造函数的原型上挂载.then方法
CustomPromise.prototype.then = function (onReaolved) {
// return 一个promise 实例
return new CustomPromise((resolve) => {
// 往回调数组中插入回调
this.callbackList.push(()=>{
const response = onReaolved(this.data)
// 判断是否是一个 CustomPromise
if(response instanceof CustomPromise){
// resolve 的权力被交给了user promise
response.then(resolve)
}else{
// 如果是普通值,直接resolve
// 依次执行callbackList里的函数 并且把值传递给callbackList
resolve(response)
}
})
})
}
写出来过后,在结合上面的那个例子使用,不能说和原生Promise
一模一样,但使用起来的链式
效果却是一毛一样。
分析说明,此过程需结合上文中的案例一起阅读
const promise1 = new CustomPromise((resolve) => {
setTimeout(() => resolve(1));
})
promise1.then((res) => {
const userPromise = new CustomPromise((resolve) => {
setTimeout(() => resolve(2), 300);
});
return userPromise
});
说明:
- 我们把
new Promise
返回的实例叫做promise1
- 在
Promise.prototype.then
的实现中,我们构造了一个新的promise
返回,叫它promise2
在调用then
方法的时候,用户手动构造了一个promise
并且返回,用来做异步的操作,叫它userPromise
,那么在then
的实现中,内部的this
其实就指向promise1
而promise2
的传入的fn
函数执行了一个this.cbs.push()
的操作,其实是往promise1
的callbackList
数组中push
了一个函数,等待后续执行
CustomPromise.prototype.then = function (onReaolved) {
// promise 2
return new CustomPromise((resolve) => {
// 往回调数组中插入回调
this.callbackList.push(()=>{})
})
}
如果用户传入给then
的onResolved
方法返回的是个userPromise
,那么这个userPromise
里用户会自己去在合适的时机 resolvePromise2
,那么进而这里的response.then(resolve)
中的resolve
就会被执行
if(response instanceof CustomPromise){
response.then(resolve)
}
再结合上面的经典案例看,我这里再放一遍
new CustomPromise((resolve) => {
setTimeout(() => {
// resolve1
resolve(1);
}, 300);
}).then((res) => {// then1
console.log(res);
// userPromise
return new CustomPromise((resolve) => {
setTimeout(() => {
// resolve2
resolve(2);
}, 300);
});
}).then(res => {// then2
console.log(res);
});
then1
这一整块其实返回的是promise2
,那么then2
其实本质上是promise2.then(()=>{})
,
也就是说then2
注册的回调函数,其实进入了promise2
的callbackList
回调数组里。
又因为我们刚刚知道,resolve2
调用了之后,userPromise
会被resolve
,进而触发promise2
被resolve
,进而 promise2
里的callbackList
数组被依次触发。
这样就实现了用户自己写的resolve2
执行完毕后,then2
里的逻辑才会继续执行,也就是异步链式调用
。
说句题外话,这个有点绕,当时还是看了好一会才看懂。
好了,当你看到这里的时候,这篇文章已经接近尾声了,是时候进行总结了。
总结
本篇文章只是根据其原理实现的一个简易链式调用的过程,真正的Promise
并没有这么简单,和上文中的比起来复杂很多,而且涉及到很多的异常、容错、边界等情况的处理。
最后推荐一下Promise A+规范
-> 点我查看规范,很值得去看,相信看完后会对Promise
有一个更深的了解。
希望本文对你有所帮助!
转载自:https://juejin.cn/post/7169138123182456862