[手写系列] 带你实现一个简单的Promise

虚幻大学 xuhss 464℃ 0评论

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475

简介

学习之前 需要先对Promise有个基本了解哦,这里都默认大家都是比较熟悉Promise的

本次将带小伙伴们实现Promise的基本功能

  1. Promise的基本骨架
  2. Promisethen
  3. Promise.then的多次调用
  4. then链式调用
  5. catch的实现
  6. finally的实现

01-搭建基本骨架

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

javascript`const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

class ZXPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED;
console.log(value);
}
}
const rejected = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED;
console.log(reason);
}
}
executor(resolve, rejected)
}
}

// 初步搭建好Promise的construtor结构
const promise = new ZXPromise((resolve, rejected) => {
resolve("123");
rejected("wushichu")
})`

  • 因为Promise有三种状态pending,fulfilled,rejected,我们这里就声明三个常量来代表这三种状态
  • Promise中需要传递一个回调函数,他的参数中包含了resolverejected,调用resolve之后,状态会变为fulfilled,调用rejected,状态会变成rejected
  • 我定义了一个类,我们在constructor中定义所需要的resolverejected函数,然后将这两个函数传入那个executor中去,这样Promise的基本骨架就已经搭建完成了,非常简单.

02-实现Promise的then功能

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

javascript`const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

class ZXPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
//因为只有pending状态才能进行变化
if(this.status!==PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED;
if (this.onfufilled)
this.onfufilled(value);
})
}
}
const rejected = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if(this.status!==PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED;
if (this.onrejected)
this.onrejected(reason);
})
}
}
executor(resolve, rejected)
}
then(onfufilled, onrejected) {
this.onfufilled = onfufilled;
this.onrejected = onrejected;
}
}

// 接下来开始写then方法
const promise = new ZXPromise((resolve, rejected) => {
resolve("123");
rejected("wushichu");
})

promise.then((res) => {
console.log("res", res);
}, (err) => {
console.log("err", err);
})`

  • then方法中接受两个参数,分别是onfulfilledonrejected两个函数,分别对应着状态fulfilledrejected
  • 这里要注意一个点我在resolverejected中都使用了queueMicrotask,这里使用的目的是为了保证顺序执行的一致性,确保在then方法执行过后,再去执行相关代码,这里需要大家熟悉微任务队列和宏任务队列,推荐大家看下这篇文章
    在JS中使用queueMicroTask

03-Promise.then多次调用

大家可以用上一部分的代码实验一下,如果多次调用,会发现只有最后一个输出,并且在定时器中使用,会出现结果为undefined

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

javascript`p1.then((res) => {
console.log("res1", res);
});

p1.then((res) => {
console.log('res2: ', res);
});

setTimeout(() => {
p1.then((res) => {
console.log("res4", res);
})
}, 1000);`
现在我们来解决下上述问题,看代码

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

javascript`const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

class ZXPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onfufilled = [];
this.onrejected = [];
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onfufilled.forEach(fn => {
fn(value);
});
})
}
}
const rejected = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onrejected.forEach(fn => {
fn(reason);
})
})
}
}
executor(resolve, rejected)
}
// 接下来为了Promise能够多次调用 进行优化
then(onfufilled, onrejected) {
if (this.status === PROMISE_STATUS_FULFILLED) {
onfufilled(this.value);
}
if (this.status === PROMISE_STATUS_REJECTED) {
onrejected(this.value);
}
if (this.status === PROMISE_STATUS_PENDING) {
this.onfufilled.push(onfufilled);
this.onrejected.push(onrejected);
}

}
}`

  • 因为改进之后,需要存储resolverejectedvaluereason值,所以我们定义了这两个值
  • 为了满足多次调用,我们需要将promise中的onfulfilledonrejected改为数组存储以用来满足我们的多次调用
  • 定时器的问题我这边说下,因为setTimeout属于宏任务,在同步代码执行完毕之后,会接着执行微任务,所以宏任务是最后来执行的,所以也就造成了promise中的代码执行完了,但是包裹在定时器中的then方法没有获取到结果
  • 所以呢,在这里我决定让处于定时器中的代码直接执行而不压入数组中去,因为定时器之前的代码已经执行完毕了,promise的状态也已经发生了改变,所以我就在then方法中判断promise的状态,如果是fulfilledrejected状态的话,传过来的函数就直接执行

04-then方法的链式调用

要想实现链式调用,那么then方法肯定是将Promise对象又给返回出来了,说到这了大家有没有思路呢?

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100

javascript`const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

class ZXPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onfufilled = [];
this.onrejected = [];
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onfufilled.forEach(fn => {
fn(value);
});
})
}
}
const rejected = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onrejected.forEach(fn => {
fn(reason);
})
})
}
}
try{
executor(resolve, rejected)
}catch(err){
console.log(err);
}

}

then(onfufilled, onrejected) {
return new ZXPromise((resolve, rejected) => {
if (this.status === PROMISE_STATUS_FULFILLED) {
try {
//如果then中有返回值,就会作为下一个then所接收的值
const value = onfufilled(this.value);
resolve(value);
} catch (err) {
rejected(err);
}

}
if (this.status === PROMISE_STATUS_REJECTED) {
try {
const value = onrejected(this.value);
resolve(value);
} catch (err) {
rejected(err);
}
}
if (this.status === PROMISE_STATUS_PENDING) {
try {
this.onfufilled.push(() => {
const value = onfufilled(this.value);
resolve(value);
});
} catch (err) {
rejected(err);
}
try {
this.onrejected.push(() => {
const value = onrejected(this.value);
resolve(value);
});
} catch (err) {
rejected(err);
}
}
})
}
}

const promise = new ZXPromise((resolve, rejected) => {
resolve("123");
rejected("wushichu");
})
promise.then((res) => {
console.log("res1:", res);
return "abc";
}, (err) => {
console.log("err1", err);
}).then((res) => {
console.log("res2", res);
}, (err) => {
console.log("err2", err);
})`

  • 变化最大的就是then方法了,大家可以看到我又把ZXPromise返回出去了,代码中我写的很清楚了

05-catch方法实现

catch方法实际上是then第二个参数的语法糖,说到这里大家有没有明白什么呢?

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

javascript`const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

const execFnWithCatchError = (execFn, value, resolve, reject) => {
try {
const result = execFn(value);
resolve(result);
} catch (err) {
reject(err);
}
}

class ZXPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onfufilled = [];
this.onrejected = [];
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onfufilled.forEach(fn => {
fn(value);
});
})
}
}
const rejected = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onrejected.forEach(fn => {
fn(reason);
})
return this.reason;
})
}
}
executor(resolve, rejected)
}

then(onfufilled, onrejected) {
//这一段是为了将错误代码传递下去的
const defaultOnRejected = err => { throw err }
onrejected = onrejected || defaultOnRejected
return new ZXPromise((resolve, rejected) => {
if (this.status === PROMISE_STATUS_FULFILLED && onfufilled) {
execFnWithCatchError(onfufilled, this.value, resolve, rejected);
}
if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
execFnWithCatchError(onrejected, this.reason, resolve, rejected);
}
if (this.status === PROMISE_STATUS_PENDING) {
if (onfufilled)
this.onfufilled.push(() => {
execFnWithCatchError(onfufilled, this.value, resolve, rejected);
});
if (onrejected) {
this.onrejected.push(() => {
execFnWithCatchError(onrejected, this.reason, resolve, rejected);
});
}
}
})
}
catch(onrejected) {
return this.then(undefined, onrejected);
}
}`

  • 大家可以看到catch代码实际上就只有一行,就是将then方法进行了调用,是不是相当简单呢
  • 然后我觉得那个try catch代码重复性比较高,所以我将它提取了出来复用
  • 然后大家看下那个then里面的开头,onrejected函数被给予了一个默认值,如果then没有传递第二个参数,那么会被赋予一个错误处理函数的默认值,抛出错误后,会自动被try catch捕获进行reject,这样子错误会被层层传递,一直到最后被catch函数所执行.

06-finally的实现

finally就是要在最后执行的函数,无论什么情况,实现起来也是非常简单

复制代码
  • 1
  • 2
  • 3

javascript finally(fn) { return this.then(() => { fn() }, () => { fn() }); }

  • 在类中加上这一段代码就好了,因为finally是无法接收任何resolve和rejected的值的,所以我们在传递的函数中执行fn,就是避免resolve的值和rejected的值被传递到finally上去

07-完整代码总览

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

javascript`const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

const execFnWithCatchError = (execFn, value, resolve, reject) => {
try {
const result = execFn(value);
resolve(result);
} catch (err) {
reject(err);
}
}

class ZXPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onfufilled = [];
this.onrejected = [];
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
this.onfufilled.forEach(fn => {
fn(value);
});
})
}
}
const rejected = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED;
this.reason = reason;
this.onrejected.forEach(fn => {
fn(reason);
})
return this.reason;
})
}
}
executor(resolve, rejected)
}

then(onfufilled, onrejected) {
//这一段是为了将错误代码传递下去的
const defaultOnRejected = err => { throw err }
onrejected = onrejected || defaultOnRejected
return new ZXPromise((resolve, rejected) => {
if (this.status === PROMISE_STATUS_FULFILLED && onfufilled) {
execFnWithCatchError(onfufilled, this.value, resolve, rejected);
}
if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
execFnWithCatchError(onrejected, this.reason, resolve, rejected);
}
if (this.status === PROMISE_STATUS_PENDING) {
if (onfufilled)
this.onfufilled.push(() => {
execFnWithCatchError(onfufilled, this.value, resolve, rejected);
});
if (onrejected) {
this.onrejected.push(() => {
execFnWithCatchError(onrejected, this.reason, resolve, rejected);
});
}
}
})
}
catch(onrejected) {
return this.then(undefined, onrejected);
}

finally(fn) {
return this.then(() => { fn() }, () => { fn() });
}
}`

  • 大家可以自行进行测试

转载请注明:xuhss » [手写系列] 带你实现一个简单的Promise

喜欢 (0)

您必须 登录 才能发表评论!