This is the 20th day of my participation in the Genwen Challenge
Developers of front-end development should all know that JavaScript is a single-threaded language, the browser only assigns JS a main thread to perform tasks, but only one task can be executed at a time, and these tasks form a task queue waiting for execution; However, some tasks are time-consuming, such as network request, event monitoring, and timer. If these time-consuming tasks are queued up for execution, the program execution efficiency will be very low, and even lead to page fake death. Therefore, browsers create new threads for these time-consuming tasks, including HTTP request threads, browser event trigger threads, and browser timing triggers, but these tasks are asynchronous, which involves the front-end development of asynchronous callback operation processing.
1. Reasons for the occurrence of Async/Await
Before ES7, knowing that Promises were ES6’s solution to asynchronous callbacks and avoid Callback Hell, why did ES7 introduce a new Async/Await standard?
The answer: Promise solved the asynchronous nesting problem by using a clear chain; But if in the actual development process in some places there are a large number of asynchronous request, and the process complicated nested case, check the code will find a more awkward situation, then, to access and modify the demanding easier, therefore ES7 puts forward new Async/Await standard is in order to solve the embarrassing situation.
But before we get into Async/Await, let’s take a closer look at promises.
Ii. Promise related content
2.1 Background of the birth of Promise
Is a new technology in ES6 to solve the asynchronous callback hell problem.
2.1.1 Callback Hell
Also called callback nested or function scrambled calls, in plain English, three network requests need to be sent, with the third request depending on the result of the second request and the second request depending on the result of the first request. Increasing use of nesting.
2.1.2 Drawbacks of callback functions
It’s a lot of work for developers to read, it’s not easy to check for errors, it’s not easy to return, and so on. Such as:
setTimeout(() => { console.log(1) setTimeout(() => { console.log(2) setTimeout(() => { console.log(3) },3000) },2000) }, 1000).Copy the code
2.1.3 Common callback functions
The application scenarios include network request, event monitoring, and timer. Common callback functions include AJAX, timer, and FS.
2.1.4 Promise resolves the asynchronous callback hell method
Promise was created to address the drawbacks of using callback functions, as shown below:
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(11), 1000);
}).then(data => console.log(data));
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(22), 2000);
}).then(data => console.log(data));;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(33), 3000);
}).then(data => console.log(data));;
}
f1().then(f2).then(f3)
Copy the code
2.2 What is Promise
What is Promise? I don’t know whether you, as a front-end developer, can correctly tell the academic concept of Promise. If not, it doesn’t matter, just look at the explanation below.
2.2.1 Promise object
A Promise is an object from which to retrieve messages for asynchronous operations, more like a container for events that will end in the future (i.e., an asynchronous operation).
The Promise object represents the ultimate success or failure of an asynchronous operation, and the resulting value, which is a proxy value, is an asynchronous callback solution in ES6.
2.2.2 Grammatical definition of Promise
Promise is a constructor that generates an instance object of a Promise.
2.2.3 Functional definition of Promise
The Promise object is used to wrap an asynchronous operation and get the result values for success and failure of the operation.
2.3 Three states of Promise
The value of the Promise object proxy is actually unknown, and the state is dynamically variable, so there are three states of the Promise object: ongoing, ended, and failed. When it runs, it can only go from ongoing to failure, or from ongoing to success. Use the Promise object whenever you run asynchronous code with a synchronous representation.
- Pending: an initial state that neither succeeds nor fails;
- Depressing: The operation is completed successfully;
- Rejected: The operation fails.
2.3.1 Basic use of Promise
The Promise constructor takes two arguments, resolve and reject, which represent the outcome of the asynchronous operation, the state in which the Promise succeeds or fails.
① The asynchronous operation is successful, the resolve function will be called, and the state of the Promise object will be changed to depressing.
When the asynchronous operation fails, call the Rejected function and change the state of the Promise object to Rejected.
As an example, the canonical way to write this is to wrap a Promise in a function and then return a Promise, as follows:
const delay = (millisecond) => { return new Promise((resolve, reject)=>{ if (typeof millisecond ! Reject (new Error(' must be number type ')); SetTimeout (()=> {resolve(' time = ${millisecond} millisecond)})}Copy the code
As you can see from the example above, Promise takes two parameters: resolve and Reject. Resolve: Changes asynchronous execution from pending(request) to resolve(success return), a function execution return; Reject: REJECT, a failure that can be returned by a function. It is recommended to return a new Error(), which is cleaner and more formal.
2.3.2 resolve function
If non-PROMISE data is passed in, a successful Promise is returned. If a Promise is passed in, the result of that object determines the return value of resolve.
Let obj =new Promise((resolve,reject)=>{resolve(' yes'); }); //1. If a non-PROMISE is passed in, then a successful Promise is returned. Let p1 = Promise. Resolve (' 123 ') / / 2. If a Promise is passed in, the result of that object determines the return value of resolve. let p2 = Promise.resolve(obj); Resolve (promise.resolve (promise.resolve (' ABC '))); console.log(p3);Copy the code
2.3.3 the rejected function
Promise.prototype.reject, always returns a failed Promise.
let p = Promise.reject(123123);
let p2 = Promise.reject('abc');
let p3 = Promise.reject(Promise.resolve('ok'));
console.log(p3);
Copy the code
2.4 Promise的API
Several commonly used methods in the Promise API are: then, catch, finally, all, race, etc. The specific usage methods are described below.
Against 2.4.1 then
Then specifies a successful or failed callback to the current Promise. Resolve then returns a Promise to use. The result returned by the THEN method is determined by the then specified callback function. Here is an example:
Let p=new Promise((resolve,reject)=>{resolve(' yes')}) p.tenn (value=>{console.log(value) // },reason=>{ console.error(reason) })Copy the code
2.4.2 catch
Catch specifies the failed callback that returns the result of the failure. Here is an example:
Let p =new Promise((resolve,reject)=>{reject(' fail! '); }) p.then(value=>{},reason=>{ console.error(reason); }) p.catch(reason=>{ console.error(reason) })Copy the code
2.4.3 finally
Finally is used to wrap things up. Whether the Promise state succeeds or fails, after the callback is executed, finally is used to find the last callback to execute. Here is an example:
Request.finally (function(){// Finally, the code that must be executed})Copy the code
2.4.4 Promise. All
When multiple Promise tasks are executed together, a new Promise is returned if all of them succeed, and a failed Promise object is returned if one of them fails. Here is an example:
Let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve(' yes'); }, 1000); }); let p2 = Promise.resolve('ok'); Let p3 = promise. reject('Oh no '); // let result = Promise. All ([p1, p2, p3]); console.log(result);Copy the code
2.4.5 Promise. Race
When multiple Promise tasks are executed synchronously, return the result of the Promise task that ends first, whether it succeeds or fails. Here is an example:
Let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve(' yes'); }, 1000); }); let p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok'); }, 500); }); let result = Promise.race([p1, p2]); console.log(result); //p2 okCopy the code
2.5 Chain call of Promise
2.5.1 Usage scenarios of chain invocation
It is common to execute two or more asynchronous operations in succession. Each subsequent operation is established after the previous operation is successfully executed and starts with the result returned by the previous operation. The operation process of constructing a Promise chain to fulfill this requirement is called chain call. Here is an example:
let p = new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'success'); }); p.then( res => { console.log(res); return new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'success'); }); } ) .then( res => console.log(res) ); // the interval is 1000ms - > success - > successCopy the code
The return value of the then object must be a Promise, and then must return a value in order to continue the call. Otherwise, undefined will be used.
2.5.2 Matters needing attention in chain writing
(1) Then — the essence of chain writing is to pass down a new Promise, that is, then in the next step to receive the Promise returned by the previous step;
② The catch statement is an error capture of the whole chain, while the then second argument is for the previous return Promise.
(3) The precedence of catch and then is determined by which operator is first to catch an error. The precedence of catch and then is determined by which operator is first to catch an error. The precedence of catch and then is determined by which operator is first to catch an error.
2.5.3 Error handling of chain writing method
When we do chain calls, we have a lot of promises, so should we have as many catches? The answer is no, because of the “bubble” nature of the chain method of error handling, any link in the chain can be caught, and at the same time, the code after a link will not be executed, so you just need to catch at the end.
If you move the catch into the first chain return, the chain will continue to go down, indicating that the catch in the chain is not the final destination. The catch is just a chain expression that catches an error, not a break.
2.5.4 Chain Return custom Value
The Promise chain returns a custom value. Use the Promise prototype resolve method directly, as shown in the following example:
Delay (1000). Then ((result)=>{console.log(' console.log '); console.log(result); Let message = 'This is the value you want to process '; Resolve (message) // Return promise.resolve (message)}). Then ((result)=>{console.log(' Step 2 completed '); console.log(result); Resolve (' return promise.resolve ')}). Catch ((err)=>{console.log(err); })Copy the code
2.5.5 Stop or Jump out of the Promise chain
Whether it was an error or an initiative to jump out of the Promise chain, you need to add a flag bit, as follows:
Return promise. reject({isterrorexpection: true)Copy the code
Async/Await related content
3.1 What is Async/Await?
Async/Await is based on Promise. Async/Await is interdependent and indispensable. They come out for Promise, which is also an evolutionary and improved version of Promise. This is to solve the asynchronous problem mentioned at the beginning of the article if a large number of complex nested promises are not easy to read.
3.1.1 Basic meaning of Async/Await
Async/Await is based on Promise implementation and is a new way to write asynchronous code. They cannot be used for ordinary callback functions.
Async/Await is also non-blocking;
③Async/Await writing makes asynchronous code look like synchronous code, concise and easy to read.
3.1.2 the grammar of the Async/Await
Async must declare a function and await must be used inside the async declared function. This is a fixed match. If any type of async does not meet the two conditions, the program will report an error.
let data = 'data'
a = async function () {
const b = function () {
await data
}
}
Copy the code
3.2 Nature of Async/Awaitd
3.2.1 Nature of Async
Async is another syntactic sugar wrapper for generator, which helps to realize generator invocation and makes the statement more similar to the expression of synchronous code. Async functions can be regarded as promise objects encapsulated by multiple asynchronous operations.
The return of an async declared function is actually a Promise, that is, as long as the declared function is async, no matter how it is handled internally, it must return a Promise, as shown in the following example:
(async function () {return 'Promise+++'})(Copy the code
3.2.2 Nature of Awaitd
The essence of await is syntactic sugar that provides the ability to wait for asynchronous return equivalent to “synchronous effect”, i.e. syntactic sugar for THEN. If you want to perform an asynchronous operation with await, the calling function must be declared with async.
Await can return a Promise object as well as a value. If await returns a Promise object, then we can continue to use the then function on the return value of await. Here’s an example:
Const a = async ()=>{let message = 'declare value 111' let result = await message; Console. log(' Wait for a while 'because the above program hasn't finished executing '); Return result} a(). Then (result=>{console.log(' output ',result); })Copy the code
3.3 Advantages of Async/Await
Why is Async/Awaitd better than Promise? The specific reasons are as follows.
3.3.1 Simplicity and clarity
As can be seen from the above examples of Async/Awaitd, Async/Awaitd is very simple to write. Compared with Promise, it does not need to write. Then, it does not need to write anonymous functions to handle Promise’s resolve value, nor define redundant data variables, and it avoids the operation of nested codes. Greatly save a lot of lines of code, making the code handling asynchronous operation concise, easy to consult and precise positioning.
3.3.2 Error handling methods
Parse will need to use. Catch in promises, but the code for error handling will be very redundant. Be aware that code is more complex in practice than it should be in theory.
Parse can handle json.parse errors with try/catch using Async/Await, as shown in the following example:
const request = async () => {
try {
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
Copy the code
3.3.3 Conditional statements
By using Async/Await, conditional statements can be written concisely and code readability can be improved. Here, I will not give a comparison example, but just one example of using Async/Await:
const request = async () => {
const data = await getJSON()
if (data.anotherRequest) {
const moreData = await anotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
Copy the code
We do the median
In actual development, this scenario is used: call promisE1, use the results returned by PromisE1 to call Promise2, and then use the results of both to call promise3. Before Async/Await is used, it should look like this:
const request = () => {
eturn promise1()
.then(value1 => {
return promise2(value1)
.then(value2 => {
return promise3(value1, value2)
})
})
}
Copy the code
After writing Async/Await, it looks like this:
const request = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}
Copy the code
From the above two writing methods, it can be seen intuitively that using Async/Await will make the code very clean, simple, intuitive and highly readable.
3.3.5 Error stack comparison
If multiple promises are called in the instance and one of them is wrong, the error stack returned in the Promise chain does not show the specific location of the error, which will lead to the time-consuming time of troubleshooting and difficulty of solving the error, and even have an adverse effect. Assume that the only function of the error stack is named errorPromise. But it’s not directly related to errors. If Async/Await is used, the error stack will directly point to the function where the error is located, so that it is more clear and intuitive for troubleshooting. Especially, it is very effective and useful when viewing the analysis error log.
3.3.6 debugging
According to the advantages of Async/Await described above, it has been repeatedly emphasized that Async/Await will make the code concise and clear. In fact, in the debugging process, Async/Await can also make the code debugging easy and simple. Compared with Promise, there is no need to write too many arrow functions. You can step directly as if debugging synchronized code, skipping the await statement.
3.3.7 Interrupt/terminate the program
The Promise itself is only a state machine, storing three states. Once a request is issued, it must be closed and cannot be canceled. Even the pending state mentioned above is only a state of pending request, but not canceled.
When Async/Await is used, it is very easy to terminate the program. That is because Async/Await is semantically obvious. It is similar to normal function writing. Essentially, it just returns a Promise. Specific examples are as follows:
let count = 3; const a = async ()=>{ const result = await delay(2000); const result1 = await delaySecond(count); if (count > 2) { return ''; // return false; // return null; } console.log(await delay(2000)); The console. The log (" end "); }; a().then(result=>{ console.log(result); }) .catch(err=>{ console.log(err); })Copy the code
The async function essentially returns a Promise.
Four, the actual development process of asynchronous operations need to pay attention to matters
In the actual front-end development process, promises or Async/Await will be used whenever asynchronous operations are involved. The previous article has analyzed the use and comparison of the two in detail. We cannot simply say that we can only use Async/Await instead of Promise. So far, these two ways to solve asynchronous callback are very effective, and the two can also be mixed, mainly according to the specific actual development requirements of the scenario.
4.1 Promise uses THEN to obtain data (serial)
In real development, if you need to iterate a request operation sequentially, you need to add delays in order to output values. If you make a mistake, you need to be more careful about how you write. The solution to the serial or parallel case is to store the function directly by the function name, which is equivalent to storing the Promise in an array in advance and executing it when it needs to be called, perfect solution.
4.2 Promise gets data through for loop (serial)
As for the Promise to get data through the for loop, first of all, let’s share a high-end notation, which is the process of iterating through the array through reduce:
array = [timeout(2000), timeout(1000), timeout(1000)] const a = array.reduce((total, current)=>{ return total.then((result)=>{ console.log(result); Return current()})}, promise.resolve (' start ')) a.hen ((result)=>{console.log(' end ', result); })Copy the code
Let me share another general way to make it easier to understand, as follows:
array = [timeout(2000), timeout(1000), timeout(1000)] const syncPromise = function (array) { const _syncLoop = function (count) { if (count === array.length - Return array[count]()} return array[count](). Then ((result)=>{console.log(result); Return _syncLoop(count+1) // Call array subscript}); } return _syncLoop(0); } syncPromise(array). Then (result=>{console.log(' complete '); SyncAll = function syncAll(){return syncPromise} promise.syncall (array).then(result=>{ console.log(result); The console. The log (' complete '); })Copy the code
4.3 Async/Await using for loop to get data (serial)
According to the for loop of the above Promise to get data for comparison, directly use the scenario of the above instance to have a look at the writing of Async/Await, the specific operation is as follows:
(async ()=>{
array = [timeout(2000), timeout(1000), timeout(1000)]
for (var i=0; i < array.length; i++) {
result = await array[i]();
console.log(result);
}
})()
Copy the code
By contrast, I will also praise Async/Await here. Intuitively, we can see the same demand. Is it very convenient and concise to use Async/Await to achieve it?
Five, the summary
Async/Await is one of the most revolutionary features to be added to JS in recent years. Async/Await lets you see how bad Promise syntax is and provides an intuitive alternative. You may have some doubts and concerns about Async/Await because Node7 is not LTS, but the code migration is easy and there is no need to worry about version stability. Also, most developers are used to using callback functions or.then to identify asynchronous code. Async/Await makes asynchronous code less “obvious” (because Async/Await makes code look like synchronous code), but this temporary discomfort will soon be eliminated after learning to use it.
In fact, the above two points are just to analyze the future trend, but Promise will not be eliminated immediately because of the emergence of Async/Await in the short term. In other words, it is with Promise that we have upgraded and improved version of Async/Await. The two are interdependent and indispensable. To learn Async/Await of front-end development well, learning Promise is the premise.
The above is all the content of this chapter. Welcome to pay attention to the wechat public account of Sanzhan “Program Ape by Sanzhan”, and the Sina Weibo account of Sanzhan “Sanzhan 666”, welcome to pay attention!