The frontispiece language
City last month, white as snow, cicada temples beauty sorrow. — Wen Tingyun (Tang dynasty) “More Leakage son meet rare”
1. Constructor
Typically we use the Promise constructor to wrap an asynchronous task and excutor’s resolve method to get the value returned by the asynchronous task.
// Typical usage
const promise = new Promise((resolve, reject) = > {
// Execute the asynchronous task in excutor. After the asynchronous task ends, use resolve to change the promise state to fullfilled
setTimeout(() = > {
resolve(100)},1000)})Copy the code
The state of a Promise object looks like this:
Initial state -- Pending indicates that the asynchronous task is pending. At present, the promise has not decided to complete the asynchronous task successfully. Fullfilled An error occurred during the completion of the asynchronous task. The fullfilled and Rejected states cannot be changed after they are specified. This kind of promise is usually referred to as the resolved promise resolve -> the promise is resolved to the fullfilled state, Reject -> resolve the promise to rejected state, and then bound the callback on the promiseCopy the code
To sum up, a Promise has three private properties:
state
- Used to save the state of the current Promise
data
- This parameter is used to save data after an asynchronous task is executed
callbacks
- Save the data to be passed before the asynchronous task is completed
Promise.then
Method specifies the callback function to be called after the resolution
- Save the data to be passed before the asynchronous task is completed
Promise’s constructor argument is a function signed (resolve, reject) => void.
resolve
- willPromiseThe resolution of
fullfilled
state - After the asynchronous task is executed
data
Stored in thePromise 中 - ifPromisethrough
then
The specified callback function before the asynchronous task completes, thenresolve
The saved callback method is placed at the end of the task queue
- willPromiseThe resolution of
reject
- willPromiseThe resolution of
rejected
state - The asynchronous task was executed incorrectly
reason
Stored in thePromise 中 - ifPromisethrough
then
The specified callback function before the asynchronous task completes, thenreject
The saved callback method is placed at the end of the task queue
- willPromiseThe resolution of
- An exception occurs
- ifexcutorIf an exception occurs during execution, thenPromiseIt automatically decides to be
rejected
State,reasonIs the exception that occurred
- ifexcutorIf an exception occurs during execution, thenPromiseIt automatically decides to be
Implementation:
constructor(excutor) {
// State of the promise
this.status = PENDING
// The data saved by the promise
this.data = undefined
// Promise pre-saved callback function
this.callbacks = []
const resolve = (value) = > {
// If the state of the promise has been decided beforehand, then nothing will be done
if(! checkStatus(this.status)) {
return
}
// 1. Switch to Resolved
this.status = RESOLVED
// 2. Save value data
this.data = value
// 3. If the callback function is to be executed, the callback is executed asynchronously immediately, that is, the callback is added to the task queue
if (this.callbacks.length > 0) {
this.callbacks.forEach(
callbacksObj= > {
setTimeout(() = > {
callbacksObj.onResolved(value)
}, 0); }}})const reject = (reason) = > {
if(! checkStatus) {return
}
this.status = REJECTED
this.data = reason
if(this.callbacks.length > 0) {
this.callbacks.forEach(
callbacksObj= > {
setTimeout(() = > {
callbacksObj.onRejected(reason)
}, 0); }}})// Catch the exception that Excutor executes, and if there is an exception, resolve the promise directly
try {
excutor(resolve, reject)
} catch (error) {
reject(error)
}
}
Copy the code
2. then
The then method is used to specify the callback function to execute after the Promise resolution. These callback functions will only be put into the callback queue for execution after the Promise resolution. If the state of a Promise is pending, these callbacks are temporarily stored in this.callbacks.
Then (onFullfilled, onRejected): Promise onFullfilled(value): any -- Promise is lowered to the fullfilled state. If this parameter is not specified, the engine automatically replaces it with (x) => x onRejected(reason): any -- Promise is used for the rejected state. If not specified, the engine will automatically replace it with reason => {throw reason}Copy the code
The THEN method is two callback functions that are called in different cases. The return value is a Promise. The state of the Promise depends on the return value of the callback function.
- The callback function returns a value of noPromise 值
- thenThe return value is one
fullfilled
The state of thePromiseAnd,valueReturns a value for the callback function
- thenThe return value is one
- The return value of the callback function is onePromise 值
- Then returns the same state as the Promise
- The return value of the callback function is onethenable
- The then return value is the Promise after this thenable calls the then method for the resolution
- An exception occurred in the callback function
- thenThe return value is one
rejected
The state of thePromise.reasonIs the exception that occurred
- thenThe return value is one
- The callback function has no return value
- thenThe return value is one
fullfilled
的 Promise.value
为undefined
- thenThe return value is one
At the same time, a Promise has two states: undecided and resolved:
- No resolution
- Save the callback function for later invocation
- Have the resolution
- Call the callback function directly
Implementation:
then(onFullfilled, onRejected) {
// The default value when onFullfilled is not specified
onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : value= > value
// Default value when onRejected is not specified
onRejected = typeof onRejected === 'function' ? onRejected : reason= > {throw reason}
return new NewPromise((resolve, reject) = > {
const handler = (callback) = > {
try {
// Execute the callback function to get the return value
const result = callback(this.data)
// If the return value is a PROMISE, the state of the promise is used as the result
if (result instanceof NewPromise) {
result.then(resolve, reject)
} else {
// The return value is a normal value
resolve(result)
}
} catch (error) {
// Throw an exception
reject(error)
}
}
// 1. In pending state
if (this.status === PENDING) {
// Save the callback to be called later in the Promise
this.callbacks.push({
onResolved: (value) = > {
handler(onFullfilled)
},
onRejected: (reason) = > {
handler(onRejected)
}
})
}else if (this.status === RESOLVED) {
// Resolved to queue the callback directly
setTimeout(() = > {
handler(onFullfilled)
}, 0)}else {
// Queue the callback directly in the onRejected state
setTimeout(() = > {
handler(onRejected)
}, 0)}}}Copy the code
3. catch
The catch method is used to handle the result of the Rejected state that appears in the Promise. Because of the exception penetration mechanism, the implementation is a special THEN call
catch(onRejected) {
return this.then(undefined, onRejected)
}
Copy the code
4. finally
Ideas:
Because both then and catch return a Promise, and the Promise could be onFullfilled or onRejected. So there are two cases to deal with.
Function signature:
finally(onfinally? : (() = > void) | undefined | null) :Promise<T>
// A callback returns a Promise
Copy the code
Implementation:
finally(callback) {
return this.then(
value= > {
// Core: discard the return value of callback execution and pass the original promise value
return NewPromise.resolve(callback()).then(
() = > {
return value
}
)
},
reason= > {
// Core: discard the reason for callback execution failure and pass the original promise reason
return NewPromise.resolve(callback()).then(
() = > {
throw reason
}
)
}
)
}
Copy the code
5. Promise.resolve()
Pormise takes a value and returns a Promise.
- If the value is notpromiseorthenable
- PromiseThe status of
onFullfilled
And has a value ofvalue
- PromiseThe status of
- If the value isPromise
- The Promise state is the same as the incoming Promise, and the value is the same
- If the value isthenable
- Execute thenable’s then method with its resolution value as the value
Implementation:
static resolve(value) {
// 1. value The value is Promise
if (value instanceof NewPromise) {
return value
} else if (value.then){
return new NewPromise((resolve, reject) = > {
value.then(resolve, reject)
})
}else {
return new NewPromise((resolve, reject) = > {
resolve(value)
})
}
}
Copy the code
6. Promise.reject()
Reject is a simple method that returns a Promise in the Reject state, with reason as the parameter value
static reject(reason) {
return new NewPromise((resolve, reject) = > {
reject(reason)
})
}
Copy the code
7. Promise.all()
Argument: an iterable – just think of it as an array for simplicity
Return value: a Promise.
- If the argument is an empty iterable, return one
fullfilled
The state of the Promise - If the parameterPromiseState is
fullfilled
, then the status of the returned value becomesfullfilled
, the obtained values are stored in an array - If the parameterPromiseOne of the states is zero
rejected
, the status of the returned value becomesrejected
The one who failedPromise 的reason
Stored in thereason
中
static all(promises) {
return new NewPromise((resolve, reject) = > {
let resolvedCount = 0
const values = new Array(promises.length)
promises.forEach((promise, index) = > {
// Details: If the parameter is not a Promise, convert it to a Promise
NewPromise.resolve(promise).then(
value= > {
values[index] = value
resolvedCount++
// Details: count to make sure all Promise states are fullfilled
if (resolvedCount === promises.length) {
resolve(values)
}
},
reason= > {
reject(reason)
}
)
})
})
}
Copy the code
8. Promise.race()
// Similar to a race, the state of a race is the state of the promise that changes its state, whether it succeeds or fails
static race(promises) {
return new NewPromise((resolve, reject) = > {
promises.forEach((promise) = > {
NewPromise.resolve(promise).then(resolve, reject)
})
})
}
Copy the code
9. Overall code
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function checkStatus(status) {
if(status ! == PENDING) {return false
}
return true
}
class NewPromise {
constructor(excutor) {
this.status = PENDING
this.data = undefined
this.callbacks = []
const reject = (reason) = > {
if(! checkStatus) {return
}
this.status = REJECTED
this.data = reason
if (this.callbacks.length > 0) {
this.callbacks.forEach(
callbacksObj= > {
setTimeout(() = > {
callbacksObj.onRejected(reason)
}, 0); }}})const doResolved = (value) = > {
// 1. Switch to Resolved
this.status = RESOLVED
// 2. Save value data
this.data = value
// 3. If the callback function is to be executed, the callback is executed asynchronously immediately, that is, the callback is added to the task queue
if (this.callbacks.length > 0) {
this.callbacks.forEach(
callbacksObj= > {
setTimeout(() = > {
callbacksObj.onResolved(value)
}, 0); }}})const resolve = (value) = > {
if(! checkStatus(this.status)) {
return
}
doResolved(value)
}
try {
excutor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFullfilled, onRejected) {
// The default value when onFullfilled is not specified
onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : value= > value
// Default value when onRejected is not specified
onRejected = typeof onRejected === 'function' ? onRejected : reason= > {throw reason}
return new NewPromise((resolve, reject) = > {
const handler = (callback) = > {
try {
// Execute the callback function to get the return value
const result = callback(this.data)
// If the return value is a PROMISE, the state of the promise is used as the result
if (result instanceof NewPromise) {
result.then(resolve, reject)
} else {
// The return value is a normal value
resolve(result)
}
} catch (error) {
// Throw an exception
reject(error)
}
}
// 1. In pending state
if (this.status === PENDING) {
// Save the callback to be called later in the Promise
this.callbacks.push({
onResolved: (value) = > {
handler(onFullfilled)
},
onRejected: (reason) = > {
handler(onRejected)
}
})
}else if (this.status === RESOLVED) {
// Resolved to queue the callback directly
setTimeout(() = > {
handler(onFullfilled)
}, 0)}else {
// Queue the callback directly in the onRejected state
setTimeout(() = > {
handler(onRejected)
}, 0)}}}catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(callback) {
return this.then(
value= > {
return NewPromise.resolve(callback()).then(
() = > {
return value
}
)
},
reason= > {
return NewPromise.resolve(callback()).then(
() = > {
throw reason
}
)
}
)
}
static all(promises) {
return new NewPromise((resolve, reject) = > {
let resolvedCount = 0
const values = new Array(promises.length)
promises.forEach((promise, index) = > {
NewPromise.resolve(promise).then(
value= > {
values[index] = value
resolvedCount++
if (resolvedCount === promises.length) {
resolve(values)
}
},
reason= > {
reject(reason)
}
)
})
})
}
static race(promises) {
return new NewPromise((resolve, reject) = > {
promises.forEach((promise) = > {
NewPromise.resolve(promise).then(resolve, reject)
})
})
}
static resolve(value) {
// 1. value The value is Promise
if (value instanceof NewPromise) {
return value
} else if (value.then){
return new NewPromise((resolve, reject) = > {
value.then(resolve, reject)
})
}else {
return new NewPromise((resolve, reject) = > {
resolve(value)
})
}
}
static reject(reason) {
return new NewPromise((resolve, reject) = > {
reject(reason)
})
}
}
Copy the code