-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMyPromise.js
More file actions
150 lines (132 loc) · 3.44 KB
/
Copy pathMyPromise.js
File metadata and controls
150 lines (132 loc) · 3.44 KB
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING
#result = undefined
#handlers = []
constructor(executor) {
let sealed = false // 只允许第一次生效
const resolve = (x) => {
if (sealed) return
sealed = true
this.#resolvePromise(x)
}
const reject = (r) => {
if (sealed) return
sealed = true
this.#changeState(REJECTED, r)
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
// ===== 内部工具 =====
isPromiseLike(value) {
return (
value !== null &&
(typeof value === 'object' || typeof value === 'function') &&
typeof value.then === 'function'
)
}
#runMicroTask(fn) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(fn)
} else if (typeof process === 'object' && typeof process.nextTick === 'function') {
process.nextTick(fn)
} else if (typeof MutationObserver === 'function') {
const ob = new MutationObserver(fn)
const text = document.createTextNode('1')
ob.observe(text, { characterData: true })
text.data = '2'
} else {
setTimeout(fn, 0)
}
}
#changeState(state, result) {
if (this.#state !== PENDING) return
this.#state = state
this.#result = result
this.#run()
}
// A+ 2.3: [[Resolve]](promise, x)
#resolvePromise(x) {
if (this.#state !== PENDING) return
if (x === this) {
this.#changeState(REJECTED, new TypeError('Chaining cycle detected'))
return
}
if (this.isPromiseLike(x)) {
// 跟随 thenable
try {
x.then(
(y) => this.#resolvePromise(y), // 递归展开
(r) => this.#changeState(REJECTED, r)
)
} catch (e) {
this.#changeState(REJECTED, e)
}
return
}
// 普通值
this.#changeState(FULFILLED, x)
}
#runOne(callback, resolve, reject) {
this.#runMicroTask(() => {
if (typeof callback !== 'function') {
// 值透传/错透传
if (this.#state === FULFILLED) resolve(this.#result)
else reject(this.#result)
return
}
try {
const data = callback(this.#result)
// 这里不直接展开,由子 promise 的 resolve 去执行 [[Resolve]]
resolve(data)
} catch (err) {
reject(err)
}
})
}
#run() {
if (this.#state === PENDING) return
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift()
if (this.#state === FULFILLED) {
this.#runOne(onFulfilled, resolve, reject)
} else {
this.#runOne(onRejected, resolve, reject)
}
}
}
// ===== 对外 API =====
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push({ onFulfilled, onRejected, resolve, reject })
this.#run()
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
return this.then(
(v) => {
if (typeof onFinally === 'function') onFinally()
return v
},
(e) => {
if (typeof onFinally === 'function') onFinally()
throw e
}
)
}
static resolve(value) {
return new MyPromise((resolve) => resolve(value)) // 交给内部 [[Resolve]] 展开
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason))
}
}