Write in front:
In the current front-end separation, we are using asynchronous methods more and more frequently, so it is very important for a qualified front-end developer to handle the return results of asynchronous methods gracefully. One of the most asked questions in the interview is how to master the Promise method. This chapter will analyze and complete a Promise method with you, hoping to help you learn.
Understand the Promise
Since we’re going to emulate ES6 promises, we need to know what this method is mostly for, what parameters it takes, what features it has, why we use promises and how we use them.
Why use it?
1. Execute AJAX logic uniformly, regardless of how to process the results, and then process the AJAX results when needed
I don’t know if you’ve thought about this, but JavaScript runs in a single thread, but if you want to handle web requests (Ajax), browser events, etc., you need to use asynchronous execution, and most of them look like this:
function callback() {
console.log('I'm a callback function');
}
console.log('Before asynchronous method');
setTimeout(callback, 1000); // Call the callback function console.log after 1 second.'After asynchronous method');
Copy the code
Then we get the following result:
Asynchronous operations trigger a function call at some point in the future, and AJAX is typical of asynchronous operations. Take jQ code as an example:
$.ajax({
type: "POST",
url: "some.php",
data: "name=John&location=Boston",
success: function(msg){
alert( "Data Saved: "+ msg ); }});Copy the code
In the above code, we can get the result of ajax operation, but this writing method is not good for us to reuse, in plain English, asynchronous processing and return results in the same block, it is not beautiful and elegant, let’s see how to handle this situation:
let p = new Promise(function (resolve, reject) {
setTimeout(() => {// Use timer to simulate asynchronous resolve(100)}, 1000); }); p.then(function (data) {
console.log(data)
})
Copy the code
As you can see, the p.chen call can be made anytime we want to get the result we just returned. Rather than having to process the results immediately when they are available in Ajax, as jQ does.
2. Support chain invocation
In the past, when we wanted to make multiple asynchronous requests, we created callback hell by accident, something like this:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: '+ finalResult); }, failureCallback); }, failureCallback); }, failureCallback);Copy the code
No doubt, the above function is to read and maintain the above let us have some difficulty, the following use Promise to implement the above code, it is much clearer:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
Copy the code
If you are careful, you will find that our error handling is concentrated in the execution of catch, which is the third feature I want to talk about
3. By catching all errors, Promise solves a fundamental flaw in the callback doom pyramid.
Having said that, I think my friends have a certain understanding of many promises, so I will implement a Promise of my own based on actual use, my own understanding and the description of PromiseA+ specification
Write conforming promises by hand
Let’s start with the code:
let p = new Promise((resolve,reject)=>{
resolve();
//reject();
})
Copy the code
As you can see from the code above, inside promise is an immediate constructor function that takes resolve and reject, so we should write our own code like this
function Promise() {
function resolve() {}function reject() { }
executor(resolve,reject)
}
Copy the code
As you can see, we have two functions resolve() and reject(), and according to the promiseA+ specification documentation:
Because the most powerful part of a promise is the THEN method, we end up passing success and failure values to then for both successes and failures. For ease of call, we use two variables to receive each value
Let’s look at the following code to see how promise works:
let p = new Promise((resolve,reject)=>{
resolve(111);
})
p.then((value) => {
console.log(value)
}, (reason) => {
console.log('err', reason);
})
Copy the code
If a promise succeeds, then the successful callback will execute immediately. If a promise fails, then the failed callback will execute immediately, so we can continue to improve our code:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
})
p.then((value) => {
console.log(value)
}, (reason) => {
console.log('err', reason);
})
Copy the code
That is, the success and failure callbacks in the THEN method are triggered after the asynchronous execution of the promise is complete, so when you call the THEN method, the state of the promise isn’t success or failure at first. Instead, we save the successful and failed callback functions and wait for the asynchrony to complete before executing the corresponding successful or failed callback, so we can write the following code:
let p = new Promise((resolve, reject) => {
resolve(111)
})
p.then((value) => {
return value+'The second time'
}, (reason) => {
console.log('err', reason);
}).then((data) => {
console.log(data)
}, () => {
})
Copy the code
After executing the code, we can easily get the printed result: 111 The second time, if the success callback of your THEN method returns a value, we can use that value in the success callback of the next THEN method. In other words, the value is passed back as an argument to the success callback of the next THEN method. Similarly, we tested if something went wrong, and found that the error was passed on to the second failure
let p = new Promise((resolve, reject) => {
resolve(111)
})
p.then((value) => {
throw new Error()
}, (reason) => {
console.log('err', reason);
}).then((data) => {
console.log(data)
}, () => {
console.log('Second get lost')})Copy the code
The printed result is: The result is understand. If you don’t want to deal with the error in the then method, you can use the catch method to catch the error. If the then method succeeds or fails without arguments, it can be an empty function. If the then method succeeds or fails without arguments, it can be an empty function.
p.then(() => {
return new Promise((resolve, reject) => {
resolve(111)
})
}, (reason) => {
}).then((data) => {
console.log('It worked',data)
}, (reason) => {
})
Copy the code
The print result is: success 111
If it returns a promise function, it will wait for the promise to complete and then return the next THEN. If the promise succeeds, it will succeed the next then, and if it fails, it will fail the next THEN. It is important to note that the callback function returned from the then method cannot be itself. If it does, then the function will wait for the result of the promise to be executed, and so the layer upon layer of state waiting can become callback hell.
let promise = new Promise((resolve,reject)=>{
resolve();
});
promise.then((value) => { // pending
return new Promise((resolve,reject)=>{
return new Promise((resolve,reject)=>{
resolve(111);
})
})
}, (reason) => {
console.log(reason);
});
Copy the code
In theory, we may draw the next then the result is: 111, because we are waiting for the promise after return, that is just our code is just the first promise is the case, judge if like the above code, can be a problem, in order to circumvent this problem, we use recursion to perform:
If you are careful, you may notice that IN the screenshot above, I also added a called interceptor, because if a white user like me, his handwritten promise can succeed or fail, then we need to determine that we can not make both calls to be executed, only the first one to be called
Now that our code is almost perfect, let’s give it a try:
let promise = new Promise((resolve,reject)=>{
resolve(1);
});
promise.then((value) => { // pending
console.log(value)
}, (reason) => {
console.log(reason);
});
console.log(2);
Copy the code
You’ll notice that our result is 1,2, but we already mentioned at the beginning of this article that promises are a function that handles asynchracy, and the result should be 2,1, because we’re still implementing promises in the current context, which is synchronization. With a few tweaks, it’s asynchronous:
Extension method implementation
Since there is also a catch method in our analysis, let’s implement it as well. Since the method can be called chain, we must also write on the prototype chain:
Promise.prototype.catch = function (onrejected) {
return this.then(null, onrejected)
}
Copy the code
Of course, promises can also be called directly using resolve() and reject(), which is an easy way to write:
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.resolve = function (value) {
returnnew Promise((resolve, reject) => { resolve(value); })}Copy the code
Write in the last
PromiseA + : Promise + : Promise + : Promise + : Promise + : Promise + : Promise + : Promise + : Promise + : Promise + : Promise + : Promise +
PS: Why incorporate the promiseA+ specification? Because we can’t write a toy code to deal with the interviewer and yourself, you need to make your code more specific readable and practical, need to avoid all kinds of problems caused by the call, make your code more watertight, in the use of scenarios will be more rich