-
Notifications
You must be signed in to change notification settings - Fork 1
/
mimi-core.js
163 lines (134 loc) · 3.94 KB
/
mimi-core.js
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
151
152
153
154
155
156
157
158
159
160
161
162
163
// The main logic of create Promise and process then callback is that:
// 1. When sync process resolve/reject function in constructor,
// don't do anything, just async process then callback function
// 2. When async process resolve/reject function in constructor,
// sync register all then callbacks to Promise instance.
// Wait for the resolve/reject function processed, and async process all
// then registered then callbaks
// Simply choose a microtask
const asyncFn = (function () {
if (typeof process === 'object' && process !== null && typeof (process.nextTick) === 'function')
return process.nextTick
if (typeof (setImmediate) === 'function')
return setImmediate
return setTimeout
}())
// States
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
// Constructor
function MimiPromise(executor) {
this.state = PENDING
this.executedData = undefined
this.multiPromise2 = []
const resolve = (value) => {
settlePromise(this, RESOLVED, value)
}
const reject = (reason) => {
settlePromise(this, REJECTED, reason)
}
executor(resolve, reject)
}
MimiPromise.prototype.then = function (resolvedCallback, rejectedCallback) {
const promise2 = new MimiPromise(() => {})
if (typeof resolvedCallback === "function") {
promise2.resolvedCallback = resolvedCallback;
}
if (typeof rejectedCallback === "function") {
promise2.rejectedCallback = rejectedCallback;
}
if (this.state === PENDING) {
this.multiPromise2.push(promise2)
} else if (this.state === RESOLVED) {
asyncProcessCallback(this, promise2, promise2.resolvedCallback)
} else if (this.state === REJECTED) {
asyncProcessCallback(this, promise2, promise2.rejectedCallback)
}
return promise2
}
MimiPromise.prototype.catch = function (rejectedCallback) {
return this.then(null, rejectedCallback)
}
// Settle any promise, set state and value, check if there is
// any then callback and async process them with the origin promise,
// return promise2(aka promise2), and callback itself.
function settlePromise(promise, executedState, executedData) {
if (promise.state !== PENDING)
return
promise.state = executedState
promise.executedData = executedData
if (promise.multiPromise2.length > 0) {
const callbackType = executedState === RESOLVED ? "resolvedCallback" : "rejectedCallback"
for (const promise2 of promise.multiPromise2) {
asyncProcessCallback(promise, promise2, promise2[callbackType])
}
}
}
// Async process callback with origin promise and promise2
function asyncProcessCallback(promise, promise2, callback) {
asyncFn(() => {
if (!callback) {
settlePromise(promise2, promise.state, promise.executedData);
return;
}
let x
try {
x = callback(promise.executedData)
} catch (e) {
settlePromise(promise2, REJECTED, e)
return
}
settleWithX(promise2, x)
})
}
function settleWithX(p, x) {
if (x === p && x) {
settlePromise(p, REJECTED, new TypeError("promise_circular_chain"));
return;
}
let xthen
const type = typeof x;
if (x !== null && (type === "function" || type === "object")) {
try {
xthen = x.then;
} catch (err) {
settlePromise(p, REJECTED, err);
return;
}
if (typeof xthen === "function") {
settleXthen(p, x, xthen);
} else {
settlePromise(p, RESOLVED, x);
}
} else {
settlePromise(p, RESOLVED, x);
}
}
function settleXthen(p, x, xthen) {
try {
xthen.call(x, (y) => {
if (!x) return;
x = null;
settleWithX(p, y);
}, (r) => {
if (!x) return;
x = null;
settlePromise(p, REJECTED, r);
});
} catch (err) {
if (x) {
settlePromise(p, REJECTED, err);
x = null;
}
}
}
MimiPromise.deferred = MimiPromise.defer = function () {
const dfd = {}
dfd.promise = new MimiPromise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = MimiPromise;