Promise
A PROMISE represents the end result of an asynchronous operation. The primary way to interact with a promise is through its THEN method, which registers a callback that receives either the final value of the promise or the reason why the promise was not satisfied.
specification
Promise A + specification
-
The term
promise
It’s the one withthen
Method objects or functions that behave in accordance with this specification.thenable
It’s a definitionthen
Method object or function.value
Any legalJavaScript
Value (includingundefined
,thenable
orpromise
).exception
Is the use ofthrow
Statement.reason
Is a value that indicates why a promise is rejected.
-
requirements
-
State of promise
-
Pending: Indicates the waiting state
- Can be converted to when in the wait state
fulfilled
/rejected
- Can be converted to when in the wait state
-
This is very depressing
- You must not transition to another state
- There must be a value that cannot be converted (
value
)
-
You never know unless you like it
- You must not transition to another state
- There must be another reason not to convert (
reason
)
-
-
A promise must provide a THEN method to access its current or final value or reason. The THEN method accepts two parameters: promise.then(onFulfilled, onRejected)
-
OnFulfilled and onRejected are both optional parameters
- if
onFulfilled
It’s not a function, it has to be ignored - if
onRejected
It’s not a function, it has to be ignored
- if
-
If onFulfilled is a function
- Must be in
promise
Complete (resolve
) is then called,promise
As its first argument - It must not be
promise
Complete (resolve
) is called before - It must not be called more than once
- Must be in
-
If onRejected is a function
- Must be in
promise
Refuse to (reject
) is then called,promise
As its first argument, the value (reason) of - It must not be
promise
Refuse to (reject
) is called before - It must not be called more than once
- Must be in
-
Onfulfilled or onRejected cannot be called until the execution context stack contains only platform code [3.1]
-
OnFulfilled and onRejected must be called as functions (without this value)[3.2]
-
A THEN on the same PROMISE can be called multiple times
- if
promise
Is completed,resolve
), all correspondingonFulfilled
Need to followthen
The order of is executed sequentially - if
promise
Is rejected,reject
), all correspondingonRejected
Need to followthen
The order of is executed sequentially
- if
-
[3.3] promise = promise.then (onFulfilled, onRejected)
- if
onFulfilled
和onRejected
Return a valuex
Run,[[Resolve]](promise2, x)
- if
onFulfilled
和onRejected
Throw an exceptione
,promise2
Must usee
Reject as a reason - if
onFulfilled
It’s not a function andpromise1
Is completed,resolve
),promise2
Need to return withpromise1
The same value (value
) - if
onRejected
It’s not a function andpromise1
Is rejected,reject
),promise2
Need to return withpromise1
The same value (reason
)
- if
-
-
The Promise resolver ([[Resolve]]) [[Resolve]] is an abstract concept that takes a Promise and x as input [[Resolve]](promise, x), if x is a thenable, It tries to make the promise take the state of X, assuming that X behaves at least a little like a promise. Otherwise, it will implement the promise with the value x. This thenable handling allows promise implementations to be more generic, as long as they expose A THEN method that adheres to promise /A+. It also allows implementations that adhere to the Promise/A+ specification to co-exist with less canonical but usable implementations, running [[Resolve]](Promise, x), performing the following steps
-
If the Promise and x point to the same object, reject the Promise with TypeError as the reason
-
If x is a promise, take its state [3.4]
- if
x
The state ispending
,promise
Must continue topending
Until thex
Be completed or rejected. - if
x
The state isresolve
Is solved with the same valuepromise
- if
x
The state isreject
The reason is the samepromise
- if
-
If x is a function or an object
- make
then
The value ofx.then
[3.5]
- If you get
x.then
An exception is thrown by the value ofe
, it wille
Reject as a causepromise
- if
then
Phi is a function of phi with phix
As athis
Call it, first argumentresolvePromise
, the second parameterrejectPromise
, including- If and when
resolvePromise
The value ofy
When running[[Resolve]](promise, y)
- if
rejectPromise
For one reasonr
Call,r
Refused topromise
- If it’s called at the same time
resolvePromise
和rejectPromise
, or the same argument is called multiple times, then the first call takes precedence and subsequent calls are ignored. (forthenable
) - If the call
then
Throw an exceptione
- if
resolvePromise
或rejectPromise
Has been called, ignore it. - Otherwise, use
e
Reject as a causepromise
- if
- If and when
- if
then
It’s not a functionx
Complete (resolve)promise
- make
-
If x is not an object or function, use x to resolve the promise
-
-
If a promise is parsed by thenable and participates in a circular thenable chain, The recursive nature of [[Resolve]](promise, thenable) eventually causes [[Resolve]](promise, thenable) to be called again, which results in infinite recursion according to the above algorithm. Implementations are encouraged (but not required) to detect this recursion and reject the promise as TypeError. [3.6]
-
annotations
-
Here the platform code makes the engine, environment, and promise implementation code. In practice, this needs to ensure that onFulfilled and onRejected are executed asynchronously and should be executed on the new execution stack after the round of event loop in which the THEN method is called. This can be accomplished with a “macrotask” mechanism such as setTimeout or setImmediate, or with a “microtask” mechanism such as MutationObserver or Process.Nexttick. Because the implementation of a PROMISE is considered platform code, a task scheduling queue may already be included when the own handler is called
-
In strict mode, this in them will be undefined; In non-strict mode, this will be a global object
-
If all requirements are met, enable PROMISe2 === PromisE1. Each implementation should record whether or not promise2 === Promise1 can be generated and when will promise2 === promise1 occur
-
In general, x is a true promise only if it comes from the current implementation. This rule allows those exception implementations to take the state of a Promise that meets known requirements
-
This program first stores a reference to x.hen, then tests that reference, and then calls that reference, thereby avoiding multiple visits to the x.hen property. Such precautions are important to ensure consistency of visitor attributes, whose values can change between two retrieves
-
Implementations should not place arbitrary limits on the depth of the Thenable chain, and assume that beyond that arbitrary limit there will be infinite recursion. Only true loops should raise a TypeError; If you encounter an infinite loop of thenable, always performing recursion is the correct behavior
-
outline
Code implementation
// ES6
// Three states
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// State: the initial state is pending
status = PENDING
/ / value
value = null
/ / reasons
reason = null
// Execute the onFulfilled queue
onFulfilledCallbacks = []
// Execute the onRejected queue
onRejectedCallbacks = []
// The constructor
constructor(executor){
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
resolve = value= > {
// Check whether the state is in the wait state
if(this.status === PENDING){
// Change the state
this.status = FULFILLED
/ / assignment
this.value = value
// Loop call
while(this.onFulfilledCallbacks.length){
this.onFulfilledCallbacks.shift()(this.value)
}
}
}
reject = reason= > {
// Check whether the state is in the wait state
if(this.status === PENDING){
// Change the status
this.status = REJECTED
// Assignment reason
this.reason = reason
// Loop call
while(this.onRejectedCallbacks.length){
this.onRejectedCallbacks.shift()(this.reason)
}
}
}
then(onFulfilled, onRejected){
// This parameter is optional
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason= > { throw reason }
const promise1 = new MyPromise((resolve, reject) = > {
// Create a function to complete the microtask
const fulfilledMicrotask = () = > {
queueMicrotask(() = > {
try{
let x = realOnFulfilled(this.value)
resolvePromise(promise1, x, resolve, reject)
}catch (error) {
reject(error)
}
})
}
// Create a microtask to execute the rejected function
const rejectMicrotask = () = > {
queueMicrotask(() = > {
try{
let x = realOnRejected(this.reason)
resolvePromise(promise1, x, resolve, reject)
}catch (error) {
reject(error)
}
})
}
// Execute directly after the status is determined
if(this.status == FULFILLED){
fulfilledMicrotask()
}else if(this.status == REJECTED){
rejectMicrotask()
}else{
// Asynchronously, join the queue
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectMicrotask)
}
})
// then returns a new promise
return promise1
}
/ / catch method
catch (onRejected) {
this.then(null, onRejected)
}
}
function resolvePromise(promise, x, resolve, reject){
if(x === promise){
// Call in a loop and report an error directly
return reject(new TypeError('The promise and the return value are the same'));
}
if(typeof x === 'function' || typeof x === 'object') {// null returns directly
if(x === null) return resolve(x)
let then
try {
then = x.then
} catch (error) {
// There is no direct rejection
return reject(error)
}
// If the object has a THEN method
if(typeof then === 'function') {let called = false
try {
then.call(x, y= > {
// Ignore multiple times
if(called) return
called = true
// Then execute
resolvePromise(promise, y, resolve, reject)
}, r= > {
// Ignore multiple times
if(called) return
called = true
reject(r)
})
} catch (error) {
//
if(called) return
called = true
reject(error)
}
}else{
// Then is not a function
resolve(x)
}
}else{
// If x is not an object or function, execute the promise with x as the argument
resolve(x)
}
}
/ / test
MyPromise.deferred = function(){
var result = {}
result.promise = new MyPromise(function(resolve, reject){
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
Copy the code
// ES5
var PENDING = 'pending';
var REJECTED = 'rejected';
var FULFILLED = 'fulfilled';
function MyPromise(executor){
this.status = PENDING
this.value = null;
this.reason = null;
this.onFulfilled = []
this.onRejected = []
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
MyPromise.prototype.resolve = function(value){
if(this.status === PENDING){
this.status = FULFILLED
this.value = value
while(this.onFulfilled.length){
this.onFulfilled.shift()(this.value)
}
}
}
MyPromise.prototype.reject = function(reason){
if(this.status === PENDING){
this.status = REJECTED
this.reason = reason
while(this.onRejected.length){
this.onRejected.shift()(this.reason)
}
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected){
var that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){ return value }
onRejected = typeof onRejected === 'function' ? onRejected : function(reason){ throw reason }
var promise1 = new MyPromise(function(resolve, reject){
var fulfilled = function(){
// setTimeout can be used without queueMicrotask
queueMicrotask(function(){
try {
var x = onFulfilled(that.value)
resolvePromise(promise1, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
var rejected = function(){
queueMicrotask(function(){
try {
var x = onRejected(that.reason)
resolvePromise(promise1, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if(that.status === FULFILLED){
fulfilled()
}else if(that.status === REJECTED){
rejected()
}else{
that.onFulfilled.push(fulfilled)
that.onRejected.push(rejected)
}
})
return promise1
}
MyPromise.prototype.catch = function(onRejected){
this.then(null, onRejected)
}
Copy the code
Execute the process
Refer to the article
Starting with a Promise interview question that gave me sleepless nights, delve into the details of how that Promise was implemented