Talking about Several Methods of Handling JavaScript Asynchronous Operation

  es6, Front end, frontend, javascript

Introduction

The asynchronous operation of js has become a commonplace topic, and articles on this topic can be found in a large number at google. Then why should I write this article? In recent work, in order to write a set of relatively complex plug-ins, various asynchronous operations need to be handled. However, for the sake of volume and compatibility, it is not planned to introduce any pollyfill, even babel is not allowed to use, which also means that it can only be handled by es5. The callback method is very unfavorable for decoupling, so we found another method to deal with this problem. The problem is that it has been solved, but it has also triggered some thoughts: What methods are there to deal with asynchronous operation of js?

I callback function

The legendary “callback hell” comes from callback functions. The callback function is also the most basic and common way to handle js asynchronous operations. Let’s look at a simple example:

First define three functions:

function fn1 () {
 console.log('Function 1')
 }
 
 function fn2 () {
 setTimeout(() => {
 console.log('Function 2')
 }, 500)
 }
 
 function fn3 () {
 console.log('Function 3')
 }

among themfn2Can be regarded as an asynchronous function with a delay of 500 milliseconds. Now I hope it can be carried out in turn.fn1,fn2,fn3. In order to ensurefn3In the final execution, we can take it asfn2Callback function for:

function fn2 (f) {
 setTimeout(() => {
 console.log('Function 2')
 f()
 }, 500)
 }
 
 fn2(fn3)

As you can see,fn2Andfn3Fully coupled together, if there are multiple similar functions, it is likely to occurfn1(fn2(fn3(fn4(...))))This kind of situation. I won’t go into details about the disadvantages of recalling hell. I believe all readers must have their own experience.

II. Event Publishing/Subscription

The publish/subscribe mode is also one of many design modes, which can handle asynchronous operations quite gracefully under es5. What is a publish/subscribe? For the example in the previous section,fn1,fn2,fn3Can be regarded as an event publisher, as long as it is executed, an event will be published. At this time, we can batch subscribe to and process an event through its subscribers, including their sequence. Following is a method of adding a message subscriber based on the example in the previous chapter (the code uses es6 for simplicity):

class AsyncFunArr {
 constructor (...arr) {
 this.funcArr = [...arr]
 }
 
 next () {
 const fn = this.funcArr.shift()
 if (typeof fn === 'function') fn()
 }
 
 run () {
 this.next()
 }
 }
 
 const asyncFunArr = new AsyncFunArr(fn1, fn2, fn3)

And then infn1,fn2,fn3Call it insidenext()Methods:

function fn1 () {
 console.log('Function 1')
 asyncFunArr.next()
 }
 
 function fn2 () {
 setTimeout(() => {
 console.log('Function 2')
 asyncFunArr.next()
 }, 500)
 }
 
 function fn3 () {
 console.log('Function 3')
 asyncFunArr.next()
 }
 
 // output =>
 // Function 1
 // Function 2
 // Function 3

As you can see, the processing order of the function is equal to the incomingAsyncFunArrThe order of the parameters of the.AsyncFunArrMaintains an array internally, each callnext()Methods all push out and execute an object stored in an array in sequence, which is also a common method in my actual work.

Iii. Promise

With Promise, there is no need to write an additional message subscriber function, just an asynchronous function that returns a Promise. Let’s look at examples:

function fn1 () {
 console.log('Function 1')
 }
 
 function fn2 () {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log('Function 2')
 resolve()
 }, 500)
 })
 }
 
 function fn3 () {
 console.log('Function 3')
 }

Similarly, we define three functions, of whichfn2Is an asynchronous function that returns Promise. now we want to execute them sequentially, just as follows:

fn1()
 fn2().then(() => { fn3() })
 
 // output =>
 // Function 1
 // Function 2
 // Function 3

There are two biggest differences between using Promise and callback. The first isfn2Andfn3Decoupling; The second is to change function nesting to chain calling, which is more friendly to developers both in semantics and writing.

IV. generator

If the use of Promise can turn callbacks into chains, then the generator method can eliminate a large number of Promise feature methods, such as a large number ofthen().

function fn1 () {
 console.log('Function 1')
 }
 
 function fn2 () {
 setTimeout(() => {
 console.log('Function 2')
 af.next()
 }, 500)
 }
 
 function fn3 () {
 console.log('Function 3')
 }
 
 function* asyncFunArr (...fn) {
 fn[0]()
 yield fn[1]()
 fn[2]()
 }
 
 const af = asyncFunArr(fn1, fn2, fn3)
 
 af.next()
 
 // output =>
 // Function 1
 // Function 2
 // Function 3

In this example, the generator functionasyncFunArr()Accept a list of functions to be executedfn, asynchronous functions will pass throughyieldTo execute. Within asynchronous functions, theaf.next()Activates the next step of the generator function.

This rough look is actually very similar to the publish/subscribe mode, which is to actively call methods inside asynchronous functions to tell subscribers to perform the next operation. However, this method is not elegant enough. For example, if there are multiple asynchronous functions, the generator function must be rewritten, and it is not very intuitive in terms of semantic level.

V. elegant async/await

Using the latest version of Node is already supported natively.async/awaitIn writing, it can also be used in old browsers through various pollyfill. Then whyasync/awaitIs the method the most elegant? Look at the code:

function fn1 () {
 console.log('Function 1')
 }
 
 function fn2 () {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log('Function 2')
 resolve()
 }, 500)
 })
 }
 
 function fn3 () {
 console.log('Function 3')
 }
 
 async function asyncFunArr () {
 fn1()
 await fn2()
 fn3()
 }
 
 asyncFunArr()
 
 // output =>
 // Function 1
 // Function 2
 // Function 3

Have you found that in defining asynchronous functionsfn2When using Promise, its content is exactly the same as before? Look at the execution function againasyncFunArr()The method of execution is very similar to that of generator.

Asynchronous operations all return Promise, and only the corresponding functions of await are required for sequential execution. this method is very friendly in semantics and simple in code maintenance-just return Promise and await, without needing to maintain the internal as generator does.yieldThe implementation of the.

Vi. conclusion

As an induction and summary, many knowledge points in this article also come from other people’s experience, but some own understanding and experience have been added. Not for popular science, but for personal accumulation. I hope that readers can put forward revised opinions and learn and progress together!