Promise
Purpose: To better use Promise everyday use.
Promise concept
Promise is a JavaScript built-in object that is also a constructor. In particular: the Promise constructor is intended to address asynchrony; Synchronized code can also be used (overkill).
As a built-in object
A static method
- Promise.all(iterable)
- Promise.allSettled(iterable)
- Promise.any(iterable)
- Promise.race(iterable)
- Promise.reject(reason)
- Promise.resolve(value)
As a constructor
Promise.prototype
- Promise.prototype.constructor
- Promise.prototype.then(onFulfilled, onRejected)
- Ondepressing is optional
- OnRejected is optional
- Promise.prototype.catch(onRejected)
- OnRejected is optional
- Promise.prototype.finally(onFinally)
- The onFinally parameter is optional
Promise instance
const promise = new Promise(function(resolve, reject) {
/ /... Omit code
if (/* Asynchronous operation succeeded */){
resolve(value);
} else{ reject(error); }});Copy the code
Instructions for executor, resolve, and reject functions
- The Promise constructor accepts one
executor
Function as argument executor
The two arguments to the function areresolve
Functions andreject
functionresolve
The Promise () function changes the state of the Promise object from “incomplete” to “successful.pending
intoresolved
), is called when the asynchronous operation succeeds, and passes the result of the asynchronous operation as a parameter;reject
The Promise () function changes the state of the Promise object from “incomplete” to “failed.pending
intorejected
), is called when an asynchronous operation fails, and an error reported by the asynchronous operation is passed as an argument.
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Copy the code
Description of the THEN method
- Once the Promise instance is generated, you can use it
then
Methods specified separatelyfulfilled
State andrejected
State callback function. then
A method can take two arguments.- Parameter Optional: Ondepressing and onRejected are both optional parameters and do not have to be provided.
- if
onFulfilled
Is not a function, which must be ignored - if
onRejected
Is not a function, which must be ignored - if
onFulfilled
Is the function:- It must be called when the promise execution ends, and its first argument is the promise’s final value, value
- It cannot be called until the promise execution ends
- It cannot be invoked more than once
- if
onRejected
Is the function:- When a promise is rejected, it must be called, and its first argument is reason, the promise’s reason
- A promise cannot be invoked until it is rejected
- It cannot be invoked more than once
then
Method returns a new Promise instance (note, not the original Promise instance). So you can write it chained, where a then method is followed by another THEN method.
Handwritten Promise constructors for better understanding promises (PromiseA+ standard)
Special note: Implement the Promise constructor with a class
1. The implementationexecutor
The function,resolve
The function,reject
function
class Promise{
/ / the constructor
constructor(executor){
/ / success
let resolve = () = >{};/ / fail
let reject = () = >{};Execute the executor function immediatelyexecutor(resolve, reject); }}Copy the code
2. Implement the state of the Promise constructor
- The Promise constructor has three states
pending
(In progress)fulfilled
(Successful)rejected
(Failed)
- The Promise constructor switches state
pending
Is the initial state- through
Resolve function
Can be converted tofulfilled
(Successful state)- On success, it cannot go to another state and must have an immutable value.
- through
Reject function
Can be converted torejected
(Failed state)- When a failure occurs, it cannot be changed to another state, and there must be an immutable reason.
- if
The executor function
If an error occurs, execute it directlyReject function
- The Promise constructor has several key properties
state
: Status descriptionvalue
: success valuereason
: Cause of failure
class Promise{
constructor(executor){
// Initialize state to wait state (state can only change once)
// It can only be changed if the state is pending
this.state = 'pending';
// Success initial value
this.value = undefined;
// Failure cause Initial value
this.reason = undefined;
// resolve, reject
const resolve = (value) = > {
// If state is not pending, the following condition is false and the block-level scoped code is no longer executed
if (this.state === 'pending') {
// State will be fulfilled successfully
this.state = 'fulfilled';
// Update the successful value
this.value = value; }};const reject = (reason) = > {
// If state is not pending, the following condition is false and the block-level scoped code is no longer executed
if (this.state === 'pending') {
// reject State changes to rejected state
this.state = 'rejected';
// Update failure cause
this.reason = reason; }};// If the executor executes a reject error, it executes reject directly, passing the error message as an input parameter
try{
executor(resolve, reject);
} catch(error) { reject(error); }}}Copy the code
3. Then method to implement Promise instance (promise.prototype.then)
- The then method takes two (optional) arguments:
onFulfilled
Functions andonRejected
function onFulfilled
The function takes a success valuevalue
onRejected
The argument to the function is the cause of failurereason
Executor functions execute synchronously
class Promise{
constructor(executor){
/ /... Omit code
}
// The then method has two parameters onFulfilled and onRejected
then(onFulfilled,onRejected) {
// Perform onFulfilled function, passing in the successful value
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
// If the status is rejected, execute the onRejected function and pass in the failure reason
if (this.state === 'rejected') {
onRejected(this.reason); }; }}Copy the code
The executor function calls resolve or reject in asynchrony
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// ondepressing function callback array
this.onResolvedCallbacks = [];
// The onRejected function callback array
this.onRejectedCallbacks = [];
const resolve = (value) = > {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// Once resolve executes, call the onFulfilled function callback array
this.onResolvedCallbacks.forEach(fn= >fn()); }};const reject = (reason) = > {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// Once reject is executed, onRejected calls back to the array
this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
executor(resolve, reject);
} catch(err) { reject(err); }}then(onFulfilled,onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
if (this.state === 'rejected') {
onRejected(this.reason);
};
// State is pending
if (this.state === 'pending') {
// The onFulfilled function is passed the onFulfilled function callback array
this.onResolvedCallbacks.push(() = >{
onFulfilled(this.value);
})
// onRejected passes the onRejected function callback array
this.onRejectedCallbacks.push(() = >{
onRejected(this.reason); })}}}Copy the code
-
The code above simultaneously implements the problem of multiple THEN calls to the same Promise instance
// Multiple THEN cases const p = new Promise(a); p.then(); p.then();Copy the code
4. Implement the chain call of the Promise instance
The then, catch, and finally of the prototype object that calls the Promise constructor all return a new Promise instance
class Promise{
constructor(executor){
/ /... Omit code
}
then(onFulfilled,onRejected) {
// Return promise2
const promise2 = new Promise((resolve, reject) = >{
if (this.state === 'fulfilled') {
const x = onFulfilled(this.value);
// This is a big promise. // This is a big promise. // This is a big promise
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'rejected') {
const x = onRejected(this.reason);
// resolvePromise () (x, promise2, onRejected)
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() = >{
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
})
this.onRejectedCallbacks.push(() = >{
const x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }}}));// Return promise, complete the chain
returnpromise2; }}Copy the code
ResolvePromise function implementation
- if
promise
andx
Point to the same object (x === promise2
), will cause a circular reference, waiting for its own completion, will report “circular reference” error TypeError as the reason to refuse to implement the promise
const p = new Promise(resolve= > {
resolve('success');
});
const p1 = p.then(data= > {
// Circular reference, waiting for their own completion, a lifetime
return p1;
})
Copy the code
- Compare X with promise2 to avoid circular applications
- if
x== null
或typeof x ! == 'object'
或typeof x ! == 'function'
directlyresolv(x)
- if
x ! = null && (typeof x === 'object' || typeof x === 'function')
- X is not a promise, directly
resolv(x)
- X is a promise that calls the then method directly
- X is not a promise, directly
function resolvePromise(promise2, x, resolve, reject){
// Loop reference error
if(x === promise2){
/ / reject an error
return reject(new TypeError('Chaining cycle detected for promise'));
}
// Prevent multiple calls
let called;
// x is not null and x is an object or function
if(x ! =null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+ specifies the then method that declares then = x
const then = x.then;
// If then is a function, the default is Promise
if (typeof then === 'function') {
// Let then execute the first argument this, followed by a successful callback and a failed callback
// This refers to x in the then method
then.call(x, y= > {
// Only one can be called for success or failure
if (called) return;
called = true;
// the result of resolve is still a promise
resolvePromise(promise2, y, resolve, reject);
}, err= > {
// Only one can be called for success or failure
if (called) return;
called = true;
reject(err);Reject (reject); reject (reject)})}else {
resolve(x); // You can do just that}}catch (e) {
// Also a failure
if (called) return;
called = true;
// Then is not validreject(e); }}else{ resolve(x); }}Copy the code
5. Improve the onFulfilled function and onRejected function in the THEN method
- This is a big pity, onFulfilled,onRejected are optional parameters, if they are not functions,
Must be ignored
.- Ondepressing is a common value, which will be directly equal to value => value when it is successful
- This is a big pity. OnRejected returns a common value. If value => value when it fails, it will run into the next onpity in the THEN
- OnFulfilled or onRejected cannot be called asynchronously, which must be fulfilled asynchronously. We’ll use setTimeout to solve the asynchronous problem
- This is a pity or onRejected. ()
class Promise{
constructor(executor){
/ /... Omit code
}
then(onFulfilled,onRejected) {
// onFulfilled if this is not a function, ignore onFulfilled and return value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
// onRejected if it is not a function, just ignore onRejected and throw an error
onRejected = typeof onRejected === 'function' ? onRejected : err= > { throw err };
let promise2 = new Promise((resolve, reject) = > {
if (this.state === 'fulfilled') {
/ / asynchronous
setTimeout(() = > {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'rejected') {
/ / asynchronous
setTimeout(() = > {
// If an error is reported
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() = > {
/ / asynchronous
setTimeout(() = > {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
});
this.onRejectedCallbacks.push(() = > {
/ / asynchronous
setTimeout(() = > {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0)}); }; });// Return promise, complete the chain
returnpromise2; }}Copy the code
Implement catch and resolve, Reject, race, and All methods
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value= > {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
executor(resolve, reject);
} catch(err) { reject(err); }}then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
onRejected = typeof onRejected === 'function' ? onRejected : err= > { throw err };
let promise2 = new Promise((resolve, reject) = > {
if (this.state === 'fulfilled') {
QueueMicrotask can be used instead of setTimeout
setTimeout(() = > {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'rejected') {
setTimeout(() = > {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() = > {
setTimeout(() = > {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
});
this.onRejectedCallbacks.push(() = > {
setTimeout(() = > {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0)}); }; });return promise2;
}
/ / catch method
catch(fn){
return this.then(null,fn); }}function resolvePromise(promise2, x, resolve, reject){
if(x === promise2){
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called;
if(x ! =null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y= > {
if(called)return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err= > {
if(called)return;
called = true; reject(err); })}else{ resolve(x); }}catch (e) {
if(called)return;
called = true; reject(e); }}else{ resolve(x); }}/ / resolve method
Promise.resolve = function(val){
return new Promise((resolve,reject) = >{
resolve(val)
});
}
/ / reject method
Promise.reject = function(val){
return new Promise((resolve,reject) = >{
reject(val)
});
}
/ / race method
Promise.race = function(promises){
return new Promise((resolve,reject) = >{
for(let i=0; i<promises.length; i++){ promises[i].then(resolve,reject) }; })}//all (get all promises, execute then, place the results in an array, and return them together)
Promise.all = function(promises){
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
i++;
if(i == promises.length){
resolve(arr);
};
};
return new Promise((resolve,reject) = >{
for(let i=0; i<promises.length; i++){ promises[i].then(data= >{
processData(i,data);
},reject);
};
});
}
Copy the code
Promise is used with async/await
let result = true;
// Get data from the server interface
async function getServerData(){
// simulate the request
const p = new Promise((resolve,reject) = >{
setTimeout(() = > {
if(result){
resolve('data')}else {
reject('err')}},1000);
})
return p
}
// For the front-end to call the interface
async function dataController() {
try {
const data = await getServerData();
console.log('data',data);
} catch (error) {
// You can catch reject
console.log('error', error); }}// Front-end interface
dataController()
Copy the code
- Special instructions
try... catch
In thecatch
Can capturereject
Function of thereason
Refer to the article
Classic INTERVIEW Questions: The most detailed handwritten Promise tutorial ever