A, specification
Let’s start with the Promise/A+ specification requirements:
Promises represent the end result of asynchronous operations. The primary way to interact with a promise is through the then method, which registers callbacks to receive the final value of a promise or the reason why the promise didn’t complete.
-
State of promise
Pormise must be one of the three states: Pending, depressing, and Rejected.
- When a promise is in pending state:
- You can switch to the fulfilled or Rejected state.
- When the promise is fulfilled:
- Cannot transition to another state.
- There must be a value and it cannot be changed.
- When the promise is in the Rejected state:
- Cannot transition to another state.
- There must be reason and it cannot be changed.
- When a promise is in pending state:
-
Then method
A promise must provide a then method that accesses the current or final value or Reason.
The then method of Pormise accepts two parameters promise. Then (onFulfilled, onRejected)
-
OnFulfilled and onRejected are both optional parameters.
- if
onFulfilled
If not a function, ignore it. - if
onRejected
If not a function, ignore it.
- if
-
If ondepressing is a function:
- It must be in
promise
被fulfilled
Later, in order topromise
的value
Called as the first argument. - It can’t be in
promise
被fulfilled
Call before. - It cannot be called more than once.
- It must be in
-
If onRejected is a function:
- It must be in
promise
被rejected
Later, in order topromise
的reason
Called as the first argument. - It can’t be in
promise
被rejected
Call before. - It cannot be called more than once.
- It must be in
-
OnFulfilled or onRejected cannot be called until the execution Context stack contains only the engine code.
-
Ondepressing or onRejected must be called as a function.
-
The then method can be called multiple times by the same promise.
- when
promise
In afulfilled
States, all of their ownonFulfilled
The callback function must be followedthen
The order of registration is called. - when
promise
In arejected
States, all of their ownonRejected
The callback function must be followedthen
The order of registration is called.
- when
-
The then method must return a promise
promise2 = promise1.then(onFulfilled, onRejected);
- if
onFulfilled
oronRejected
Return a valuex
, the implementationPromise Resolution Procedure[[Resolve]](promise2, x)
. - if
onFulfilled
oronRejected
An exception is throwne
,promise2
Have to bee
As reason, go to the Rejected state. - if
onFulfilled
It’s not a function, andpromise1
Is in a depressing state, thenpromise2
Must be withpromise1
The same value will be fulfilled. - if
onRejected
It’s not a function, andpromise1
If the rejected state is in the rejected statepromise2
Must be withpromise1
There is no reason but rejected.
- if
-
-
Promise Resolution Procedure
The Promise Resolution Procedure is an abstract operation. It takes a promise and a value as input, called: [[Resolve]](promise, x). If x is a Thenable, it will try to make the Promise the same state as X, provided x is a similar Promise object. Otherwise, it will make the promise move to a fulfilled state with x as value.
This thenables treatment allows different Promises to interoperate as long as they expose A THEN method that complies with Promises/A+. It also allows Promises/A+ implementations to “assimilate” inconsistent implementations using sound THEN methods.
[[Resolve]](promise, x) Do the following:
-
If the promise and X reference the same object, a TypeError is used as reason to make the promise rejeted.
-
If x is also a Promise, make the promise accept its state
- if
x
Is in a pending state,promise
Must remain pending untilx
This will become a pity or rejected state.promise
Synchronously change. - If or when
x
This is a big pity. I will gradually forget the song with the same valuepromise
This will become a depressing state. - If or when
x
In the rejected state, use the same reasonpromise
It’s also the Rejected state.
- if
-
If x is an object or a function.
-
Let then equal x. teng.
-
If x. Chen is read and raises an exception e, use eas reason to change the Promise state to Rejected.
-
If then is a function, call it with x as this, passing in the first argument resolvePromise and the second argument rejectPromise.
-
If resolvePromise is called in y, execute [[Resolve]](promise, y)
-
If rejectedPromise is called by r, use r as reason to change the promise state to Rejected
-
If both resolvePromise and rejectPromise are called, or more than once. Only the first call is valid, the rest is ignored.
-
If the call to THEN throws an exception e,
- if
resolvepromise
或rejectPromise
If it has already been called, ignore it. - Otherwise, in order to
e
As a reason to letpromise
Change to the Rejected state.
- if
-
-
If then is not a function, use x as value to make the promise become a fulfilled state.
-
-
If x is not an object or function, use x as value to make the promise become a fulfilled state.
-
If a promise is resolved by an object in a loop thenable chain and the recursive nature of [[Resolve]](promise, thenable) causes it to be called again, it will be stuck in infinite recursion according to the algorithm above. While not mandatory, the algorithm encourages implementers to detect the presence of such recursions and reject promises with TypeError as reason.
Second, the implementation
- Implement the Promise class using class
- Use class private fields
- use
queueMicrotask
replacesetTimeout
Implement asynchronous microtasks
// promise.js
/* Promise /A+ promise */
class Promise {
static #FULFILLED = 'fulfilled'
static #REJECTED = 'rejected'
static #PENDING = 'pending'
static #resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
throw new TypeError('Chaining cycle detected for promise')}if ((typeof x === 'object'&& x ! = =null) | |typeof x === 'function') {
let called
try {
const then = x.then
if (typeofthen ! = ='function') resolve(x)
else {
then.call(
x,
(value) = > {
if (called) return
called = true
Promise.#resolvePromise(promise2, value, resolve, reject)
},
(reason) = > {
if (called) return
called = true
reject(reason)
}
)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
#state = Promise.#PENDING
#result = null
#onResolveCallbacks = []
#onRejectCallbacks = []
constructor(executor) {
if (typeofexecutor ! = ='function') {
return new TypeError(`Promise resolver ${executor} is not a function`)}try {
executor(this.#resolve.bind(this), this.#reject.bind(this))}catch (e) {
this.#reject(e)
}
}
#resolve(value) {
if (this.#state === Promise.#PENDING) {
this.#state = Promise.#FULFILLED
this.#result = value
this.#onResolveCallbacks.forEach((cb) = > cb())
}
}
#reject(reason) {
if (this.#state === Promise.#PENDING) {
this.#state = Promise.#REJECTED
this.#result = reason
this.#onRejectCallbacks.forEach((cb) = > cb())
}
}
then(onFulfilled, onRejected) {
if (typeofonFulfilled ! = ='function') {
onFulfilled = (value) = > value
}
if (typeofonRejected ! = ='function') {
onRejected = (reason) = > {
throw reason
}
}
const promise2 = new Promise((resolve, reject) = > {
if (this.#state === Promise.#PENDING) {
this.#onResolveCallbacks.push(() = > {
queueMicrotask(() = > {
try {
const x = onFulfilled(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.#onRejectCallbacks.push(() = > {
queueMicrotask(() = > {
try {
const x = onRejected(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
if (this.#state === Promise.#FULFILLED) {
queueMicrotask(() = > {
try {
const x = onFulfilled(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.#state === Promise.#REJECTED) {
queueMicrotask(() = > {
try {
const x = onRejected(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
})
return promise2
}
}
// Implement the following functions and implement NPX Promises -aplus-tests
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) = > {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
Copy the code
Three, validation,
- Save the file as
promise.js
- Run the following command in the directory to verify
$ npx promises-aplus-tests promise.js
Copy the code
Reference:
- juejin.cn/post/700177…