Promise is a solution to asynchronous programming that makes more sense and is more powerful than traditional solutions — callback functions and events. It was first proposed and implemented by the community, and ES6 has written it into the language standard, unifying usage, and providing Promise objects natively. @ ruan teacher
Let’s look atpromise
Several uses of:
- Promise.prototype.then: The then method takes two arguments. The first argument is a callback that will be executed when the Promise succeeds, the second callback that will be executed when the Promise fails, and the then method returns a new Promise instance
new Promise((resolve, reject) => resolve()).then(() => {
console.log('success');
}, () => {
console.log('err')})Copy the code
- Promise.prototype.catch: Used to specify the callback function when an error occurs
If reject is not specified, the catch function is finally called: new Promise((resolve, reject) => {reject('err');
})
.then()
.catch(e => {
console.log(e); // => Error: err
})
Copy the code
- Promise.resolve: a method defined in the Promise class itself that can be called promise.resolve (), which directly changes the state to a success state
Promise.resolve('hahaha').then(data => { console.log(data); / / = >'hahaha'
})
Copy the code
- Promise.reject: A method defined in the Promise class itself that can be called through promise.reject (), effectively changing the state directly to fail
Promise.reject('hahaha').then(data => { console.log(data); / / = >'hahaha'
})
Copy the code
- Promise.prototype.all: Returns the results of multiple Promise implementations in an array
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise1');
}, 1500);
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise2'); }, 2000); }) Promise.all([promise1, promise2]).then(data => { console.log(data); / / = > ["promise1"."promise2"]})Copy the code
- .
Let’s implement a fit ourselvesPromiseA + specificationThe Promise of
Let’s start with a simple promise:
console.log(1)
new Promise(() => {
console.log(2)
});
console.log(3)
Copy the code
Since we all know that promises are asynchronous, they should print 1,3,2 and then we run them and find that they print 1,2, and 3 when we use then:
console.log(1)
Promise.resolve().then(() => {
console.log(2);
})
console.log(3)
Copy the code
The result is the same as we expected, with 1,3, and 2 being printed because the Promise callback executes immediately and only the THEN method is asynchronous
A promise must be in one of three states: pending, fulfilled, or rejected.
- This is a big pity. Promise must have three states pending(fulfilled), which must be fulfilled(rejected).
- When the state is
pending
when- This state can be changed to another state (depressing or Rejected).
- When the state is
fulfilled
when- Cannot transition to another state
- There has to be one
value
(Value after success)
- When the state is
rejected
when- Cannot transition to another state
- There has to be one
reason
(Cause of failure)
- When the state is
Class Promise {constructor(executor) {// Each Promise instance has its own three states. We use a variable to hold the current state this.status ='pending'; // This. Value; // This. Reason; // The method used to change the current state to the successful stateletResolve = val => {// State and value can be changed only if the current state is' pending 'if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled'; }} // The method used to change the current state to a failed stateletReject = Reason => {// State and value can be changed only if the current state is' pending 'if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected'; } // Execute executor(resolve, reject) {// execute executor(resolve, reject) {// execute executor(resolve, reject); } catch (e) {reject(e) {reject(e) {reject(e); }}}Copy the code
then
methods
A promise must provide a then method to get the current or final value or reason
The then method of Promise has two parameters, which are onFulfilled when it succeeds and onFulfilled when it fails
onFulfilled
和onRejected
Both are optional parameters- If not, we need to set a default method
- if
onFulfilled
Is afunction
- It must be present
promise
The state of becomingfulfilled
And then it gets called, and puts the promise’svalue
(the value after success) as its first argument - The state is
fulfilled
You couldn’t call it before - Can only be called once
- It must be present
- if
onRejected
Is afunction
- It must be present
promise
The state of becomingrejected
And then it gets called, and puts the promise’sreason
(Cause of failure) as his first parameter - The state is
rejected
You couldn’t call it before - Can only be called once
- It must be present
promise.then(onFulfilled, onRejected)
Copy the code
class Promise { constructor(executor) {... }then(onFulfilled, onRejected) {// If the current state is successful, we will perform the successful callback and pass the saved successful valueif (this.status === 'fulfilled') { onFulfilled(this.value); } // If the current state is failed, we execute the failed callback and pass the saved failure reasonif (this.status === 'rejected') { onRejected(this.reason); }}}Copy the code
Okay, so now we’re ready to test it out
new Promise((resolve, reject) => {
resolve('perfect'); }).then(data => { console.log(data); // => perfect}Copy the code
Perfect, but then the question is, what if our resolve or reject is asynchronous?
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('perfect');
}, 1000);
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})
Copy the code
OnFulfilled and onRejected will not print something, that means they are not executed. Let me think…
This is because if our resolve or reject is asynchronous, when our THEN executes, the state is still pending, so of course nothing executes. We can save onFulfilled and onRejected first, and perform the corresponding callback successively when the state changes. A: Is that it? B: Yes, that’s the subscription publishing model. Let’s rewrite this:
class Promise {
constructor(executor) {
this.status = 'pending'; this.value; this.reason; // This. OnSuccessCallback = []; // This. OnErrorCallback = [];let resolve = val => {
if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled'; / / state changes Executed in sequence to the success of this callback. OnSuccessCallback. ForEach (fn = > fn ()); }}let reject = reason => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected'; / / state changes Failed to perform, in turn, the callback this. OnErrorCallback. ForEach (fn = > fn ()); } // Execute executor(resolve, reject) {// execute executor(resolve, reject) {// execute executor(resolve, reject); } catch (e) {reject(e) {reject(e) {reject(e); }}then(onFulfilled, onRejected) {
if (this.status === 'fulfilled') {
onFulfilled(this.value);
}
if (this.status === 'rejected') {
onRejected(this.reason);
}
if (this.status === 'pending') {/ / to save success callback callbacks and failure were to wait for state changes, and in turn perform corresponding method enclosing onSuccessCallback. Push (() = > {onFulfilled (enclosing the value). }); this.onErrorCallback.push(() => { onRejected(this.reason); }); }}}Copy the code
-
OnFulfilled and onRejected are both asynchronous calls.
-
OnFulfilled and onRejected must be performed as a function
-
The then method must return a promise
promise2 = promise1.then(onFulfilled, onRejected) Copy the code
- if
onFulFilled
oronRejected
A value is returnedx
, run the Promise resolver - if
onFulfilled
oronRejected
An exception was throwne
.promise2
State must berejected
And wille
As aonRejected
The parameters of the - if
onFulfilled
Is not afunction
andpromise1
isfulfilled
.promise2
Must also befulfilled
And use andpromise1
The samevalue
- if
onRejected
Is not afunction
andpromise1
isrejected
.promise2
Must also berejected
And use andpromise1
The samereason
We often use promises.then ().then().then().then()… This is called a chained call, so how do you proceed with the then method, which the specification says must return an instance of a promise, in order to implement the chained call
class Promise { contructor() {... }then(onFulfilled, onFulfilled) {// If onFulfilled and onFulfilled are not a function, we will give a default value onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function'? onRejected : err => { throw err }; // We package everything into a Promise instance and return that instance so we can implement the chained callletpromise2; Promise2 = new Promise((resolve, reject) => {// this is a big pity.if (this.status === 'fulfilled') { setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'rejected') { setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'pending') {/ / to save success callback callbacks and failure were to wait for state changes, and in turn perform corresponding method enclosing onSuccessCallback. Push (() = > {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); this.onErrorCallback.push(() => {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); }});returnpromise2; }}Copy the code
If there is a return value x, run the promise resolver resolvePromise.
Let’s start with the specification:
The promise resolver is a function that takes promise2 and x as parameters
- if
-
If promise2 and x are a promise, raise a TypeError
-
If x is an object or function
- Use the variable
then
storagex.then
- if
x.then
Will throw an exceptione
, calling promise’sreject
And wille
As its argument - if
then
Is afunction
, the use ofcall
It’sthis
Point to thex
And its first argument isresolvePromise
The second argument isrejectPromise
:- when
resolvePromise
bey
The parser continues to execute when the value is called - when
rejectPromise
When executed, the promise’s is calledreject
And will fail reasonsr
As its argument
- when
- if
then
Is not aobject
orfunction
, calling promise’sresolve
And will bex
As its argument
- Use the variable
-
If x is not an object or function, call the Promise’s resolve and take x as its argument
There are only two points:
-
If promise2 is equal to x, it raises a TypeError, so let’s look at that first
letP = new Promise((resolve, reject) => {// Return the current Promise instancereturn p; }); p.then(data => { console.log(data); }, err => { console.log(err); }); Copy the code
Running the above code, we’ll see that the Promise throws an exception that tells us TypeError: Chaining cycle detected for promise, this is because the success or failure of P depends on itself and it waits for its own execution results, so it will neither succeed nor fail
- if
onFulFilled
oronRejected
A value is returnedx
, run the Promise resolverresolvePromise
The return value x could be a constant, it could be an object, it could be a promise, and what this program does is if x is a promise, it resolves x all the way to the constant
letp = new Promise((resolve, reject) => { resolve(new Promise((resolve, reject) => { resolve(1111); }})))Copy the code
letResolvePromise = (promise2, x, resolve, reject) => {// Throw a type error if promise2 and x are equalif (promise2 === x) { return reject(new TypeError('wrong')); } // Only one resolvePromise call is allowedletcalled; // Continue parsing if x is not a constantif(x ! == null && (typeof x ==='function' || typeof x === 'object') {// We might get an error when we call x. Chen, because we might use a try {from someone else's Promise library.let then= x.then; / / ifthenIs a function, prove that x is a promise, and keep parsingif (typeof then= = ='function') { then.call(x, y => { if (called) { return; } else { called = true; } resolvePromise(promise2, y, resolve, reject); }, r => { if (called) { return; } else { called = true; } reject(r); })}else{// indicate that x may be a normal object, not a promise resolve(x); } } catch (e) {if (called) { return; } else { called = true; } reject(e); }}elseResolve resolve(x); }}Copy the code
Resolve Promise. Reject Promise. All
class Promise { contructor {... }then() {... Reject (value) {// Promise. Reject (value) {return new Promise((resolve) => { resolve(value); }) } static reject(reason) { returnnew Promise((resolve, reject) => { reject(reason); })} // Static all(promises) {returnNew Promise(resolve, reject) => {// Save the resultlet arr = []; let index = 0; const saveData = (i, data) => { arr[i] = data; if(++index === promises.length) { resolve(arr); }}for (let i = 0; i < promises.length; i++) { promises[i].then(data => { saveData(i, data); }, reject) } }) } } Copy the code
Okay, let’s do a full version of promise
class Promise { contructor() { this.status = 'pending'; this.value; this.reason; // This. OnSuccessCallback = []; // This. OnErrorCallback = [];let resolve = val => { if (this.status === 'pending') { this.value = val; this.status = 'fulfilled'; / / state changes Executed in sequence to the success of this callback. OnSuccessCallback. ForEach (fn = > fn ()); }}let reject = reason => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; / / state changes Failed to perform, in turn, the callback this. OnErrorCallback. ForEach (fn = > fn ()); } // Execute executor(resolve, reject) {// execute executor(resolve, reject) {// execute executor(resolve, reject); } catch (e) {reject(e) {reject(e) {reject(e); } } static resolve(value) {return new Promise((resolve) => { resolve(value); }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promises) { returnNew Promise(resolve, reject) => {// Save the resultlet arr = []; let index = 0; const saveData = (i, data) => { arr[i] = data; if(++index === promises.length) { resolve(arr); }}for (let i = 0; i < promises.length; i++) { promises[i].then(data => { saveData(i, data); }, reject) } }) } then(onFulfilled, onFulfilled) {// If onFulfilled and onFulfilled are not a function, we will give a default value onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function'? onRejected : err => { throw err }; // We package everything into a Promise instance and return that instance so we can implement the chained callletpromise2; Promise2 = new Promise((resolve, reject) => {// this is a big pity.if (this.status === 'fulfilled') { setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'rejected') { setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'pending') {/ / to save success callback callbacks and failure were to wait for state changes, and in turn perform corresponding method enclosing onSuccessCallback. Push (() = > {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); this.onErrorCallback.push(() => {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); }});returnpromise2; }}letResolvePromise = (promise2, x, resolve, reject) => {// Throw a type error if promise2 and x are equalif (promise2 === x) { return reject(new TypeError('wrong')); } // Only one resolvePromise call is allowedletcalled; // Continue parsing if x is not a constantif(x ! == null && (typeof x ==='function' || typeof x === 'object') {// We might get an error when we call x. Chen, because we might use a try {from someone else's Promise library.let then= x.then; / / ifthenIs a function, prove that x is a promise, and keep parsingif (typeof then= = ='function') { then.call(x, y => { if (called) { return; } else { called = true; } resolvePromise(promise2, y, resolve, reject); }, r => { if (called) { return; } else { called = true; } reject(r); })}else{// indicate that x may be a normal object, not a promise resolve(x); } } catch (e) {if (called) { return; } else { called = true; } reject(e); }}elseResolve resolve(x); }}Copy the code
- if