In our work, promises are used to solve asynchronous callbacks. A lot of libraries or plug-ins that I use usually use promise, such as AXIos, FETCH, etc. But do you know how a promise is written?
PromisesA + (promisesA+) (promisesA+) (promisesA+) (promisesA+)
1. Promise’s statement
First of all, a promise must be a class, so we declare it as a class.
- Due to the
new Promise((resolve, reject)=>{})
, so pass a parameter (function),secretThey call it Executor, pass it in, execute it. - Executor takes two arguments, called resolve and reject.
- Because resolve and Reject are executable, they are both functions that we declare with a let.
class Promise{
/ / the constructor
constructor(executor){
/ / success
let resolve = (a)= >{};/ / fail
let reject = (a)= >{};// Execute immediatelyexecutor(resolve, reject); }}Copy the code
Resolve the base state
secretTo have a rule on a Promise:
-
This is very depressing. There are three states: state pending, fulfilled and rejected
-
This is very depressing. Pending is the initial state and can be changed to fulfilled or rejected.
-
Upon success, there must be no transition to another state, and there must be an immutable value
-
On failure, no change to another state must occur, and there must be an immutable reason.
-
New Promise((resolve, reject)=>{resolve(value)}) The resolve parameter is successful. The value parameter is received. The state changes to fulfilled and cannot be changed again.
-
New Promise((resolve, reject)=>{reject(reason)}) {reject(resolve)=>{reject(reason)})
-
Reject () if an error occurs in the executor function.
So we get the following code
class Promise{
constructor(executor){
// Initialize the state to wait
this.state = 'pending';
// The value of success
this.value = undefined;
// Cause of failure
this.reason = undefined;
let resolve = value= > {
// If state changes, the resolve call will fail
if (this.state === 'pending') {
// After the resolve call, state is converted to the successful state
this.state = 'fulfilled';
// Store the successful value
this.value = value; }};let reject = reason= > {
// Reject calls fail when state is changed
if (this.state === 'pending') {
// Reject after the call, state is converted to the failed state
this.state = 'rejected';
// The cause of the storage failure
this.reason = reason; }};// Reject is rejected if an error occurs
try{
executor(resolve, reject);
} catch(err) { reject(err); }}}Copy the code
Then method
secretThis will be fulfilled someday. This method is called “then”, which has two parameters: onFulfilled,onRejected, a successful value and a failed reason
- This is fulfilled. If the state is fulfilled, onFulfilled is executed, passing this.value. If state is rejected, then onRejected is executed, passing this.reason
- If they are functions, they must be called after fulfilled, rejected, respectively, with value or reason as their first argument
class Promise{
constructor(executor){... }// This method has two parameters: onFulfilled onRejected
then(onFulfilled,onRejected) {
This is fulfilled. Perform onFulfilled and pass in the successful value
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
// If the status is rejected, execute onRejected and pass in the failure reason
if (this.state === 'rejected') {
onRejected(this.reason); }; }}Copy the code
This is the beginning of martial arts, can deal with the river’s lake small miscellaneous hair, but for the bandit with setTimeout or no.
Solving asynchronous implementations
We can do simple synchronization code now, but when resolve is executed in a setTomeout and then state is still pending, we need to store success and failure in a separate array when we call THEN, and call them once either reject or resolve
Similar to publish/subscribe, we store the two functions in the THEN. Since a promise can have multiple THEN’s, they are stored in the same array.
// Multiple then cases
let p = new Promise(a); p.then(); p.then();Copy the code
ForEach calls them on success or failure
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// The array that was successfully stored
this.onResolvedCallbacks = [];
// Failed to store the method array
this.onRejectedCallbacks = [];
let resolve = value= > {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// Once resolve executes, call the function on the successful array
this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// Once reject is executed, call the failed array function
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);
};
// When the state is pending
if (this.state === 'pending') {
// this will be fulfilled
this.onResolvedCallbacks.push((a)= >{
onFulfilled(this.value);
})
// onRejected is passed into the failure array
this.onRejectedCallbacks.push((a)= >{
onRejected(this.reason); })}}}Copy the code
Resolve chained calls
We use it a lotnew Promise().then().then()
, this is the chained call to solve callback hell
1. To achieve the chain, we return a promise in the first then by default. Promise2 = New Promise((resolve, reject)=>{})
- Pass the value returned by promisE2 to the next THEN
- If an ordinary value is returned, the ordinary value is passed to the next THEN
2, when we return an argument in the first THEN This return will produce a new promise which is the value of onFulfilled() or onRejected()
The secret specifies the value of onFulfilled() or onRejected(), the first value returned by then, which is called x, and the function that determines x is called resolvePromise
- First, see if X is a promise.
- If it is a Promise, take its result as the successful result of the new Promise2
- If it is a common value, it is used as a successful result of PromisE2
- So compare X to PROMISe2
- The resolvePromise parameters are promise2 (the promise returned by default) and x (ourselves)
return
), resolve, reject - Resolve and reject are in Promise2
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) {
// Declare the returned promise2
let promise2 = new Promise((resolve, reject) = >{
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value);
// The resolvePromise function deals with the return promise and the default promise2
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'rejected') {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push((a)= >{
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
})
this.onRejectedCallbacks.push((a)= >{
let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }}}));// Return promise to complete the chain
returnpromise2; }}Copy the code
Complete the resolvePromise function
The cheater specifies a piece of code that allows different Promise codes to be interchanged, called a resolvePromise
- If x === promise2; if x == promise2; if x == promise2
let p = new Promise(resolve= > {
resolve(0);
});
var p2 = p.then(data= > {
// Loop reference, wait for yourself to complete, lifetime can not complete
return p2;
})
Copy the code
1, judge x
- Otherwise, if x is an object or function,Let then be x.then
- X cannot be null
- So x is a normal value so resolve(x)
- X is an object or function (including promise),
let then = x.then
2, when x is an object or function (default promise) - The statement then
- Reject () reject() reject() reject()
- If then is a function, then we use call to execute then, taking this as the first argument, followed by successful and failed callbacks
- 3. Only one success and failure call can be made, so set a called to prevent multiple calls
function resolvePromise(promise2, x, resolve, reject){
// Circular 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+ specify, declare then = x
let then = x.then;
// If then is a function, the default is promise
if (typeof then === 'function') {
// Let then execute the first argument with this followed by a successful callback and a failed callback
then.call(x, y => {
// Only one success or failure call can be made
if (called) return;
called = true;
// The result of resolve is still a promise
resolvePromise(promise2, y, resolve, reject);
}, err => {
// Only one success or failure call can be made
if (called) return;
called = true;
reject(err);// Failure is failure})}else {
resolve(x); // Direct success}}catch (e) {
// This is also a failure
if (called) return;
called = true;
// If the "then" error occurs, do not continue the executionreject(e); }}else{ resolve(x); }}Copy the code
Solve other problems
This is very depressing. If they are not functions, they must be ignored
- OnFulfilled returns a normal value, which is directly equal to on success
value => value
- This is very depressing, this is very depressing, this is very depressing, this is very depressing, this is very depressing, this is very depressing, this is very depressing
reason => throw err
2,secretSpecifies that onFulfilled or onRejected cannot be called synchronously and must be called asynchronously. Let’s use setTimeout to solve the asynchronous problem - This is very depressing. If onFulfilled or onRejected, return reject()
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) {
This function will be fulfilled fulfilled. If this function is not fulfilled, omit onFulfilled and return value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
// if onRejected is not a function, you can ignore onRejected and throw error
onRejected = typeof onRejected === 'function' ? onRejected : err= > { throw err };
let promise2 = new Promise((resolve, reject) = > {
if (this.state === 'fulfilled') {
/ / asynchronous
setTimeout((a)= > {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'rejected') {
/ / asynchronous
setTimeout((a)= > {
// If an error occurs
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push((a)= > {
/ / asynchronous
setTimeout((a)= > {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
});
this.onRejectedCallbacks.push((a)= > {
/ / asynchronous
setTimeout((a)= > {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0)}); }; });// Return promise to complete the chain
returnpromise2; }}Copy the code
You’re done
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') {
setTimeout((a)= > {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'rejected') {
setTimeout((a)= > {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push((a)= > {
setTimeout((a)= > {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0);
});
this.onRejectedCallbacks.push((a)= > {
setTimeout((a)= > {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(e) { reject(e); }},0)}); }; });return promise2;
}
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, put the results in an array, 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
How to verify that our promise is correct
1. Add the following code to the end first
NPM I promises-aplus-tests -g will make promises to MAC users with sudo in front of them
Promises -aplus-tests [js file name] promises-aplus-tests [js file name
// The current is through his test he will test a subject
/ / syntactic sugar
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve,reject) = >{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
// NPM install Promises -aplus-tests to test if promises do not comply with promisesA+ specifications
Copy the code