1. What is Promise
Remember the first time I heard JS there was this thing, the instinctive Chinglish translation, promise? If I move a code, what do I promise? Now looking back, its state immutable feature also fits the name. Maybe this is the romance of programmer!! A: I don’t believe it, Ali. What’s the matter? Rich men go bad. I can’t, what I need is money.
Ok, back to the point, let’s believe in the beauty of love, let’s see what this Promise is
Promise is a solution to asynchronous programming that is more reasonable and powerful than traditional callback functions and events
1.1 Causes of Promise and pain points to solve
In a real project, if we have a situation where we need to execute a second network request based on the result of the first network request, and then execute a third request with the result of the second request…
Code that doesn’t use promises looks something like this:
request1 (function (Request the results1) {request2 (function (Request the results2) {request3 (function (Request the results3) {... })})})Copy the code
Look like this actually ok, not very horrible! But if the business requirements were more complex, the request would keep piling up. To make matters worse, in practice, the request data is processed on every request, so the code becomes ugly and bloated, and basically unreusable. This is the famous callback hell
JS asynchrony is implemented through callback functions. Write the second part of the task in a separate function. When the first part has the result and the second part needs to be executed, call the callback function directly. Promise was introduced to solve the problem of callback hell, not as a new syntactic feature, but as a new way of writing that allows nested callback functions to become chained calls.
In the 21st century, coding must have some pursuit, must be elegant. So a lot of the big guys are trying to solve this problem with a more elegant way of organizing code to solve the asynchronous nesting problem. With synchronous writing like the following in mind, the Promise specification was born.
letRequest the results1= request1(a);letRequest the results2= request2Request result1);
letRequest the results3= request3Request result2); .// A request can also be reused
letRequest the results4= request3Request result1);
letRequest the results5= request2Request result3);
Copy the code
Of course, Promise also has its drawbacks. The biggest problem with Promise is that it has redundant code, a lot of then, and the original semantics are not clear. We’ll talk about that later.
So what is the Promise specification
1.2 Promise specification
Promise constructor:
The Promise object is a constructor that generates a Promise instance
// Create a Promise instance
const promise = new Promise(function (resolve, reject) {
// ... some code
if (/* Asynchronous operation succeeded */) {
resolve(value)
} else {
reject(error)
}
})
Copy the code
The Promise constructor takes a function as an argument, resolve and reject. These are two functions. The resolve function changes the state of the Promise object from “unfinished” to “successful.” It will be called when the asynchronous operation succeeds and will pass the result of the asynchronous operation as an argument. The Reject function changes the state of the Promise object from “unfinished” to “failed” (i.e., from Pending to Rejected). It is called when the asynchronous operation fails and passes the error reported by the asynchronous operation as a parameter.
I promise.
new Promise(request1). Then (request2Request result1). Then (request3Request result2). Then (request4Request result3). Then (request5Request result4))
.catch(...// Handle exception)
Copy the code
Compare this notation to the callback notation above. It’s not hard to see that promises are written more intuitively and can catch exceptions from asynchronous functions in their outer layers.
What are some common approaches Promise uses?
Methods:
1. Promise.resolve
A Promise object must reslove a value before it can be accepted by a subsequent THEN. The function in then returns either a result or a new Promise object (then itself returns a new Promise, and if there is no return data, the next THEN receives undefined). To be received by subsequent THEN callbacks
let p = new Promise((reslove) = > {
reslove(2)
// return 2 cannot be passed to the following then
})
p.then(v= > v).then(v= > console.log(v)) / / 2
Copy the code
2. Promise.reject
3. Promise.race
Multiple Promise tasks are executed simultaneously, returning the result of the first completed Promise task executed, regardless of whether the Promise result succeeds or fails
4. Promise.all
Wrap multiple Promise instances into a new Promise instance. Const p = promise.all ([p1, p2, p3]) Multiple Promise tasks are executed simultaneously. If all are successfully executed, the results of all Promise tasks are returned as an array. If there is a Promise task Rejected, only the result of the Rejected task is returned. The.then method is called when all the Promise objects in the array become resolve or have a Rejected state, and they are executed concurrently.
Let’s see how Promise. All is used
- Receives an array of Promise instances or an object with an Iterator interface.
- Resolve becomes a Promise object if the element is not a Promise object
- If everything succeeds and the state becomes resolved, the returned values will be passed as an array to the callback
- Once there is a failure, the state changes to Rejected and the return value is passed directly to the callback.
- The return value of all() is also the new Promise object.
function promiseAll(promises) {
return new Promise(function (resolve, reject) {
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}
let promiseLength = promises.length;
let resolveCounter = 0;
let resolveValues = new Array(promiseLength);
for (let i = 0; i < promiseLength; i++) {
Promise.resolve(promises[i]).then(function (value) {
resolveCounter++;
resolveValues[i] = value;
if (resolveCounter == promiseLength) {
returnresolve(resolveValues); }},function (err) {
returnreject(err); })}})}Copy the code
Here is a reference to the big guy’s article implementing promise.all
How to return all data even if there is a failed state? Append.then to each Promise object passed “all”
Promise.all([
Promise.resolve(1).then((res) = > ({ status: 'suc', res }), (err) = > ({ status: 'err', err })),
Promise.reject(2).then((res) = > ({ status: 'suc', res }), (err) = > ({ status: 'err', err })),
Promise.resolve(3).then((res) = > ({ status: 'suc', res }), (err) = > ({ status: 'err', err }))
]).then(res= > console.log(res), err= > console.log(err))
// [{status: "suc", res: 1},{status: "err", err: 2},{status: "ok", res: 3}]
// Function encapsulation
function handlePromise(promises) {
return promises.map(promise= >
promise.then(res= > ({ status: 'suc', res }), err= > ({ status: 'err', err }))
)
}
Promise.all(handlePromise([Promise.resolve(1), Promise.reject(2), Promise.resolve(3)]))
.then(res= > console.log(res),err= >console.log(err))
Copy the code
Here is not a specific way to use each class method, these can see Ruan Yifeng ES6 learning. Please click here
Instance methods:
- Promise.prototype.then
The effect is to add a callback to the Promise instance when its state changes.
The first argument is the callback function to the Reslove state and the second argument (optional) is the callback function to the Rejected state
The then method returns a new Promise instance, so you can use the chain notation
The return value of the then arguments can be one of three
return
A synchronized value, orundefined
Returns by default if no valid value is returnedundefined
);
Return an Resolved Promise object with a synchronized value or undefined
return
Another Promise,then
Method creates a new Promise object return based on the state and value of the Promisethrow
A synchronization exception,then
Method will return onerejected
The Promise of the state, whose value is the exception
.then(() = >{...return 2;
return Promise.resolve(2); // Same as above
})
Copy the code
- Promise.prototype.catch
Rejection is an alias of. Then (null, Rejection) or. Then (undefined, Rejection) that specifies the callback when an error occurs
1.3 Advantages and disadvantages of Promise
advantages
-
Unified asynchronous API.
-
The problem of callback hell is solved by expressing asynchronous operations in a synchronous flow.
-
Chained calls are a big advantage of Promises; event handling cannot be chained.
-
Better error handling is introduced.
disadvantages
-
Promises cannot be cancelled and will be executed as soon as they are created. And calling resolve or Reject does not terminate the execution of the Promise’s argument function.
new Promise((resolve, reject) = > { resolve(1); console.log(2); }).then(r= > { console.log(r); }); // 2 1 console.log(2) Copy the code
-
Without setting a callback, errors thrown internally by a Promise will not be reflected externally.
-
When in the Pending state, there is no way to know whether progress is just beginning or about to end.
-
By the time the Promise actually performs the callback, the part of the code that defines the Promise has actually run out, so the Promise’s error stack is context-unfriendly.
1.4 Implementation of Promise
Simply implement several important properties
function myPromise(constructor) {
let self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
function resolve(value) {
if (self.status === 'pending') {
self.value = value;
self.status = 'resolved'; }}function reject(reason) {
if (self.status === 'pending') {
self.reason = reason;
self.status = 'rejected'; }}// Catch a construction exception
try {
constructor(resolve, reject)}catch (e) {
reject(e)
}
}
myPromise.prototype.then = function (onFullfilled, onRejected) {
let self = this;
switch (self.status) {
case 'resolved':
onFullfilled(self.value);
break;
case 'rejected':
onRejected(self.reason);
break;
default:}}Copy the code
This is just a simple imitation of promises. The full implementation mechanism is complex and needs to be drilled in. There are a lot of articles online
- Promise Implementation Principle (I) — Basic implementation
- Promise implementation principle (source code attached)
- See a Promise implementation from a Promise execution sequence
2. The application promise
2.1 Promise features
- Immediate execution
- Internal state machine, three states
- Irreversibility of states
- Chain calls
- Then callback asynchrony, microtasks, then callbacks are executed alternately
- Promise. Resolve () returns a value
- Resolve, reject
The Promise feature Click Here is shown in question 8
2.2 async/await
See async function here for more details
And promise the difference
- Definition,
-
A Promise is an object that represents the eventual completion or failure of an asynchronous operation and its resulting value
-
Async function is a declaration statement that defines an asynchronous function that returns an AsyncFunction object, which returns its result with an implicit Promise.
-
Await is an expression used to suspend the execution of the current asynchronous function and wait to await the execution of the await Promise object.
- Interrupt mechanism
-
Inside Promise is a state machine that can’t be stopped once it runs
-
Async function interrupts a program with await
Relations with promise
The purpose of async/await is to simplify the synchronous behavior of using multiple Promises and to perform certain operations on a set of Promises.
Promises are like structured callbacks, async/await is more like a combination of Generators and Promise
Usage scenarios
Generally used in asynchronous operations, used together
-
Promise provides utility functions corresponding to scenarios such as promise.all and implements a set of Promises
-
Async/await avoids complex Promise chained calls and is more semantic.
For example, if we have a requirement that three separate requests are successful and the data is changed, we need to get the latest data when the request is successful and then execute another function.
function 1 () { return. newPromise }
function 2 () { return. newPromise }
function 3 () { return. newPromise }
funciton 4() {...// handle after 1,2,3}
// Trigger the above function here
async function click () {
await 1(a);await 2(a);await 3(a);4()}Copy the code
2.3 Promises and event loops
The code inside the Promise executes immediately. But Promise.then is a microtask, and complex chain calls test the analysis of event loops. Also note the scope of influence of return. For more analysis, see the following topic
1. Execution order of the Promise chain call
So let’s look at the chain call of Promise by looking at mom’s cooking and by writing convention, log from top to bottom of mom’s cooking
new Promise((resolve, reject) = > {
console.log('Mama's going to cook');
resolve();
}).then(() = > {
console.log('Shopping for food');
new Promise((resolve, reject) = > {
console.log('To the vegetable Market');
resolve();
}).then(() = > {
console.log('Buy food');
}).then(() = > {
console.log('home');
})
}).then(() = > {
console.log('cooking');
})
Copy the code
So what is the output of this problem? Here, serve!
Mother wants to cook and buy food to go to the market to cook food homeCopy the code
What!!!!!! Don’t mother love me, in the outside of the meal also don’t give me to eat? Impossible!!
The first time to see this topic is wrong, and then quickly read the answer, feel that they will, feel that they have learned new knowledge, and become stronger!!
Then saw this problem, sure enough, and wrong.
Here involved more JS unknown little secret, suddenly realized, do not understand these secrets, that I will never understand this problem.
See this article for an in-depth look at the Promise microtask registration and execution process
The first question of the article is expanded
new Promise((resolve, reject) = > {
console.log("External promise");
resolve();
})
.then(() = > {
console.log("External first THEN");
return new Promise((resolve, reject) = > {
console.log("Internal promise");
resolve();
})
.then(() = > {
console.log("Inside the first THEN");
})
.then(() = > {
console.log("Internal second THEN");
})
.then(() = > {
console.log("Inside the third then");
})
.then(() = > {
console.log("Inside the fourth THEN");
})
})
.then(() = > {
console.log("External second THEN");
});
Copy the code
Output: Lazy… Print it out in written order
Reason: The first THEN returns a new Promise, and most importantly, the four inner THEN are returned together. The next THEN in the outer code must wait until the return code completes and the Promise state changes. If the return is not completed, the first THEN is not completed, and the external second THEN is waiting.
Wait until the execution of the fourth internal THEN completes.
Proof: Add a return value to the new Promise and each THEN, depending on which return value the second external THEN accepts. You will find that you receive the return value of the fourth internal THEN, and all the others are undefined. If you’re interested, you can open the comments and test it yourself.
new Promise((resolve, reject) = > {
console.log("External promise");
resolve();
})
.then(() = > {
console.log("External first THEN");
return new Promise((resolve, reject) = > {
console.log("Internal promise");
resolve('ss');
})
.then(() = > {
console.log("Inside the first THEN");
// return 1;
})
.then(() = > {
console.log("Internal second THEN");
// return 2;
})
.then(() = > {
console.log("Inside the third then");
// return 3;
})
.then(() = > {
console.log("Inside the fourth THEN");
return 4;
})
})
.then((r) = > {
console.log(r);
});
Copy the code
2. Alternate execution of the Promise chain call
- A Promise
new Promise((resolve, reject) = > {
console.log("External promise");
resolve();
})
.then(() = > {
console.log("External first THEN");
new Promise((resolve, reject) = > {
console.log("Internal promise");
resolve();
})
.then(() = > {
console.log("Inside the first THEN");
})
.then(() = > {
console.log("Internal second THEN");
})
.then(() = > {
console.log("Inside the third then");
})
.then(() = > {
console.log("Inside the fourth THEN");
})
})
.then(() = > {
new Promise((resolve, reject) = > {
resolve();
})
console.log("External second THEN");
});
Copy the code
The output
External promises external first then internal Promises internal first then external second then internal second then internal third then internal fourth thenCopy the code
- More than one Promise
new Promise(resolve= > {
resolve()
}).then(() = > {
return new Promise(r= > {
console.log(1-1 ' ');
r()
}).then(() = > {
console.log('1-1 p1')
})
}).then(() = > {
console.log('2')})new Promise(resolve= > {
resolve(2);
}).then(() = > {
console.log('2-1');
}).then(() = > {
console.log('2-2');
}).then(() = > {
console.log('2-3');
})
Copy the code
The output
1-1
2-1
1-1 p1
2-2
2-3
1-2
Copy the code
As you can see from the above two cases, promise. then is executed alternately.
I have a question, because of the previous return Promise, why is 1-2 output last?
3. return Promise.resolved
Equivalent to several microtasks
new Promise(resolve= > {
resolve()
}).then(() = > {
return new Promise(r= > {
console.log('promise');
r(5)
})
}).then(r= > {
console.log(r)
})
new Promise(resolve= > {
resolve(2);
}).then(() = > {
console.log('1');
}).then(() = > {
console.log('2');
}).then(() = > {
console.log('3');
}).then(() = > {
console.log('4');
})
// promise 1 2 3 5 4
Copy the code
If there is no return, r will output before 2. Return delayed output by two microtasks.
At the end of this article, there is an in-depth introduction to the Promise microtask registration and implementation process. Thank you very much for this article, so I won’t copy it. The drainage?
2.4 Promise other question types
Updated later…
Refer to the article
- Es6 – promise nguyen
- Interview selection Promise
- Eight pieces of code to master Promise completely
- See a Promise implementation from a Promise execution sequence
- Let’s talk about Promise
- An in-depth look at the Promise microtask registration and execution process