手写一个Promise

Promise在现在已经不是新鲜事,是前端必须要掌握的语法了,用的话几乎每天都在用,那么如果要我们实现一个Promise又应该怎么做呢?

了解Promise

先认识下Promise,Promise是解决JS回调地狱的方法之一,异步的实现方式之一。

Promise的三个状态

  • resolve 表示耗时操作已经完成
  • reject 有异常捕捉到,操作结束
  • pending 还没有调用到resolve或者reject代码块依然执行中,仍在等待结果

基础的Promise使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const findSomeThing = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
},3000);
})
}

const demo = () => {
findSomeThing().then((res) => {
console.log('Say HI')
}).catch((err) => {
throw new Error(err)
})
}

为了更清晰,这里就不用await语法了。可以看到有以下几点:

  1. 我们的findSomeThing就是耗时的异步操作
  2. 然后返回一个Promise,Promise代码块中在耗时操作完成之后,会执行到resolve或者reject
  3. 然后就会执行到我们的then函数中的代码块或者直接catch到异常。
  4. 还有一点要注意,Promise是可以形成链式操作的,就是可以同时有多个then。

清晰了主要用法,就可以想想怎么去实现

参考Promises/A+规范

开撸

首先先定义一个Promize类:

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
function Promize (executor) {
return new _Promize (executor)
}

function _Promize (executor) {
this.STATUS = {
PENDING: 'Pending', // 等待态
FULFILLED: 'Fulfilled', // 执行态
REJECTED: 'Rejected' // 拒绝态
}

this.status = this.STATUS.PENDING
this.thenQueue = []
this.catchExecutor = null
// 保证链式操作以后才执行
setTimeout(() => {
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (err) {
this.reject.call(this, err)
}
},0)
}

_Promize.prototype.resolve = function (data) {
if (this.status === this.STATUS.PENDING) {
this.status = this.STATUS.FULFILLED
let thenTask = this.thenQueue.shift()
if (thenTask) {
this.callbackData = data
thenTask()
}
}
}

_Promize.prototype.reject = function (err) {
this.status = this.STATUS.REJECTED
if (this.catchExecutor) {
this.catchExecutor(err)
}
}

_Promize.prototype.catch = function (errorExecutor) {
this.catchExecutor = errorExecutor
return this
}

_Promize.prototype.then = function (task) {
this.thenQueue.push(() => {
let res = task(this.callbackData)
if (res) {
this.callbackData = res
}
this.next()
})
// 链式调用
return this
}

_Promize.prototype.next = function () {
if (this.thenQueue.length > 0) {
let thenTask = this.thenQueue.shift()
thenTask()
}
}

// Test
Promize(function(resolve, reject){
setTimeout(() => {
console.log('done')
resolve('123')
},2000)
}).then((data) => data+'2') // then 处理返回结果
.then((data) => console.log(data)) // 输出返回结果
.catch((err) => console.log('err:'+ err))