Skip to content

AHABHGK

JavaScript

参考工业聚:100 行代码实现 Promises/A+ 规范

1const isFunction = obj => typeof obj === 'function'
2const isObject = obj => !!(obj && typeof obj === 'object') // null 的情况
3const isThenable = obj => (isFunction(obj) || isObject(obj)) && 'then' in obj && isFunction(obj.then)
4const isPromise = promise => promise instanceof Promise
5
6const PENDING = 'pending'
7const FULFILLED = 'fulfilled'
8const REJECTED = 'rejected'
9
10class Promise {
11 constructor(fn) {
12 this.status = PENDING
13 this.value = undefined
14 this.reason = undefined
15 this.onFulfilledCallbacks = []
16 this.onRejectedCallbacks = []
17 function resolve(value) {
18 if (this.status !== PENDING)
19 return
20 setTimeout(() => {
21 this.status = FULFILLED
22 this.value = value
23 this.onFulfilledCallbacks.forEach(cb => cb(this.value))
24 }, 0)
25 }
26 function reject(reason) {
27 if (this.status !== PENDING)
28 return
29 setTimeout(() => {
30 this.status = REJECTED
31 this.reason = reason
32 this.onRejectedCallbacks.forEach(cb => cb(this.reason))
33 })
34 }
35 try {
36 fn(resolve, reject)
37 } catch (e) {
38 reject(e)
39 }
40 }
41
42 then(onFulfilled, onRejected) {
43 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
44 onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
45 return bridgePromise = new Promise((resolve, reject) => {
46 if (this.status === FULFILLED) {
47 setTimeout(() => {
48 try {
49 let result = onFulfilled(this.value)
50 resolvePromise(bridgePromise, result, resolve, reject)
51 } catch (e) {
52 reject(e)
53 }
54 }, 0)
55 } else if (this.status === REJECTED) {
56 setTimeout(() => {
57 try {
58 let result = onRejected(this.reason)
59 resolvePromise(bridgePromise, result, resolve, reject)
60 } catch (e) {
61 reject(e)
62 }
63 }, 0)
64 } else if (this.status === PENDING) {
65 this.onFulfilledCallbacks.push(() => {
66 try {
67 let result = onFulfilled(this.value)
68 resolvePromise(bridgePromise, result, resolve, reject)
69 } catch (e) {
70 reject(e)
71 }
72 })
73 this.onRejectedCallbacks.push(() => {
74 try {
75 let result = onRejected(this.reason)
76 resolvePromise(bridgePromise, result, resolve, reject)
77 } catch (e) {
78 reject(e)
79 }
80 })
81 }
82 })
83 }
84
85 catch(onRejected) {
86 return this.then(null, onRejected)
87 }
88
89 static resolve(p) {
90 if (isPromise(p)) return p
91 return new Promise((resolve, reject) => {
92 if (isThenable(p)) p.then(resolve, reject)
93 else resolve(p)
94 })
95 }
96
97 static reject(p) {
98 return new Promise((_, reject) => reject(p))
99 }
100
101 static all(promises) {
102 return new Promise((resolve, reject) => {
103 let values = []
104 let count = 0
105 function handle(value, index) {
106 values[index] = value
107 if (++count === promises.length) resolve(values)
108 }
109 // p 可能不是 Promise,所以用 Promise.resolve 包一下
110 promises.forEach((p, i) => Promise.resolve(p).then(value => handle(value, i), reject))
111 })
112 }
113
114 static race(promises) {
115 return new Promise((resolve, reject) => {
116 promises.forEach(p => Promise.resolve(p).then(resolve, reject))
117 })
118 }
119
120 static allSettled(promises) {
121 return new Promise((resolve) => {
122 let results = []
123 let count = 0
124 function handle(result, index) {
125 results[index] = result
126 if (++count === promises.length) resolve(results)
127 }
128 promises.forEach((p, i) => Promise.resolve(p).then(
129 value => handle({ status: 'fulfilled', value }, i),
130 reason => handle({ status: 'rejected', reason }, i),
131 ))
132 })
133 }
134}
135
136function resolvePromise(bridgePromise, result, resolve, reject) {
137 if (bridgePromise === result) {
138 return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
139 }
140 if (isPromise(result)) {
141 if (result.status === PENDING) {
142 result.then(y => resolvePromise(bridgePromise, y, resolve, reject), reject)
143 } else {
144 result.then(resolve, reject)
145 }
146 } else if (isThenable(result)) {
147 result.then(y => resolvePromise(bridgePromise, y, resolve, reject), reject)
148 } else {
149 resolve(result)
150 }
151}