@js-rocks/promise-tiny v0.1.0
Promise A+ TDD implementation
Resources
- specification : https://promisesaplus.com/
- test cases: https://github.com/promises-aplus/promises-tests
Implementation details
Producer && consumer
- producer: resolve, reject
consumer: then, catch, finally
all
- race
- Promise.resolve, promise.reject
Macro/Micro task
The built-in Promise in the browser uses micro-task
, we can only use macro-ask
simulation, here we use setTimeout
resolve delayed binding
Resolve must be executed in the next event cycle, otherwise the onFulfilled
and onRejected
functions in then have not been bound, and if the following code does not add setTimeout, an error will be reported
// app
let promise = new MPromise(resolve => {
resolve('a-ok')
})
promise.then(promiseSpy)
resolve(value) {
// setTimeout(() => {
this.state = 1
this.value = value
this.onFulfilled(value)
// })
}
The binding of this
Resolve and reject will be executed in the executor scope, and this
needs to be bound to the current instance
Promise chain
The then line of each promise becomes the starting point of a single promise chain
let promise = new MPromise(resolve => {
resolve(42);
});
promise.then(firstSpy); // start point of chain1
promise.then(secondSpy); // start point of chain2
promise
.then(firstSpy)
.then('..')
.then('..'); // the continuous extension of the chain
Then returns another promise to form a promise chain, and at the same time, the current promise queue needs to save the pointer of the next promise
then(onFulfilled = () => {}, onRejected) {
let promise = new MPromise(() => {})
// save the pointer of the next promise
this.pending.push([promise, onFulfilled, onRejected])
if (this.state === 1) {
this.scheduleQueue(this.value)
}
return promise
}
In this way, the resolve of the next promise is called when the current promise resolves, forming a chain reaction
while (this.pending.length) {
let [promise, onFulfilled, onRejected] = this.pending.shift();
// 触发链条上的下一个promise
promise.resolve(this.value);
}
Promise returns promise
The key point is where in the chain this promise is inserted
The original thought was to process it in the scheduleQueue function
if (isFunction(onFulfilled)) {
let returnResult = onFulfilled(this.value)
if (isFunction(returnResult instanceof MPromise)) {
// 当前promise作为consumer, returnResult作为producer
return promise.then(
() => {
return returnResult.resolve(returnResult.value)
},
() => {
return returnResult.reject(returnResult.value)
}
)
} else {
promise.resolve(returnResult)
}
Finally found that it can only be processed in the resolve function.
When the value is returned, the resolve of the value(promise) will be called at some point in the future.
At this point, our current promise has been suspended.
When the resolve of value is called, the value will be passed to the resolve of promise, and the promise continues.
resolve(value) {
if (value && isFunction(value.then)) {
value.then(this.resolve.bind(this), this.reject.bind(this))
} else {
this.state = 1
this.value = value
this.scheduleQueue()
}
}
1 year ago