Sorting out several network request modes of js -- getting rid of callback hell

abstract
This paper introduces three writing methods of asynchronous network requests based on XMLHttpRequest, Promise and async/await. async/await allows us to write asynchronous programs in a way similar to synchronization and get rid of cumbersome callback functions.

1, Background

In order to meet more and more testing requirements and reduce repetitive work, Youdao intelligent hardware testing group has developed a series of testing efficiency improvement tools based on electron.

The programming language of electron is js, because everyone is not a professional front-end and is not familiar with js. They stepped on a lot of holes when writing programs. Especially events and network requests in js, which involve asynchronous programming, are easy to make mistakes.

With the rapid development and iteration of tools, more and more nested callback functions appear in the code, and the probability of tool crash is increasing. In order to solve these problems, we use async/await to reconstruct these callback functions, which reduces the amount of code and greatly improves the readability and understandability of the code.

This paper introduces three writing methods of asynchronous network requests based on XMLHttpRequest, Promise and async/await. async/await allows us to write asynchronous programs in a way similar to synchronization and get rid of cumbersome callback functions.

2, Foreword

In js, if it's not complicated to initiate a single network request, you can use fetch, axios or XMLHttpRequest directly to meet the requirements.

But if multiple requests pull data in order, it will be very troublesome to write 😂, Because the network requests in js are asynchronous, the most common way to execute them sequentially is to initiate the next request in the callback function, as shown in the following codes:

const requestOptions = {
    method: 'GET',
    redirect: 'follow'
};

fetch('https://xxx.yyy.com/api/zzz/', requestOptions)
    .then(response => response.json())
    .then(data => {
        fetch('https://xxx.yyy.com/api/aaa/'+data.id, requestOptions)
            .then(response => response.json())
            .then(data => {
                console.log(data)
            })
            .catch(error => console.error('error', error));
    })
    .catch(error => console.error('error', error));

Suppose I need to go through two steps to obtain a data, such as from https://xxx.yyy.com/api/zzz/ Get a data object data through data ID gets the serial number of the data I want to get, and then sends a request to get the desired data.

The way of using callback function is similar to the above, which is too cumbersome and error prone, and it is difficult to change once the logic is complex.

Next, sort out several network request modes of js, get rid of the callback hell, and hope to help small partners with similar problems.

(1) XMLHttpRequest

The first is XMLHttpRequest, which is mainly referred to by the famous Ajax when learning the front end. The routine of creating network request through XMLHttpRequest object is as follows:

// Hypothetical access http://localhost:3000/user Return json object {"name":"YouDao"}
const xhr = new XMLHttpRequest();
const url = 'http://localhost:3000/user'

xhr.onreadystatechange = function(){
  if (this.readyState == 4 && this.status == 200){
    const json=JSON.parse(xhr.responseText)
    const name=json.name
    console.log(name)
  }
}
xhr.open('GET',url)
xhr.send()

This code first creates an XMLHttpRequest object xhr, and then gives it to xhr Onreadystatechange add the callback function of readystatechange event, and then xhr Open ('GET ', url) initializes the request, and xhr Send() sends the request.

After the request is sent, the program will continue to execute without blocking, which is also the advantage of asynchronous call. When the browser receives a response, it will enter XHR In the callback function of onreadystatechange. Throughout the request, XHR Onreadystatechange will be triggered four times, and each readyState will increase from 1 to 4. The final response data can be obtained only in the final stage, that is, when the readyState is 4. After reaching the fourth stage, it is also necessary to judge whether the status code of the response is normal according to the status. Generally, the response code is 200, indicating that the request has not encountered a problem. This code will eventually type YouDao on the console.

It can be seen that when processing requests through XMLHttpRequest, first create an XMLHttpRequest object for each request, and then bind the callback function of readystatechange event to each object. If multiple requests are connected in series, it's troublesome to think about it.

(2) Promise

Promise was introduced in ECMAScript 2015. If an event depends on the result returned by another event, using callback will make the code very complex. Promise object provides a mode to check the failure or success of an operation. If successful, another promise will be returned. This makes the writing of callback more standardized.

The routine processed by Promise is as follows:

const promise = new Promise((resolve,reject)=>{
  let condition = true;
  if (condition) {
    resolve("ok")
  } else {
    reject("failed")
  }
}).then( msg => console.log(msg))
  .catch( err => console.error(err))
  .finally( _ =>console.log("finally"))

The above code concatenates the whole process. First, create a Promise object. Its constructor receives a function. The first parameter of the function is the function resolve to be executed when there is no error, and the second parameter is the function reject to be executed after the error.

resolve refers to the callback function in then after successful execution, and reject refers to the callback function executed in catch after failed execution. finally, it will be executed regardless of success or failure. It can be used to do some final cleaning work.

Promise based network requests can be implemented with axios library or fetch provided by the browser.

The routine of axios library creation request is as follows:

import axios from 'axios'
const url = 'http://xxx.yyy.com/'
axios.get(url)
  .then(data => console.log(data))
  .catch(err => console.error(err))

I prefer to use fetch. Fetch is a browser API used to replace XMLHttpRequest. It does not need a guide library. The way fetch creates requests is similar to axios. It has been shown at the beginning and will not be written repeatedly.

Although Promise simplifies the writing method of callback function, it still doesn't get rid of the callback hell. If multiple requests are strung together, a new Promise will be created in then, which will eventually become Promise hell, as I wrote at the beginning.

(3) async/await

async/await was introduced in ECMAScript 2017, which can simplify the writing method of Promise, so that asynchronous function calls in the code can be executed in sequence and easy to understand.

Let's use the example at the beginning to illustrate:

Get data directly with fetch:

const requestOptions = {
    method: 'GET',
    redirect: 'follow'
};

fetch('https://xxx.yyy.com/api/zzz/', requestOptions)
    .then(response => response.json())
    .then(data => {
        fetch('https://xxx.yyy.com/api/aaa/'+data.id, requestOptions)
            .then(response => response.json())
            .then(data => {
                console.log(data)
            })
            .catch(error => console.error('error', error));
    })
    .catch(error => console.error('error', error));

After rewriting with async/await:

async function demo() {
​  const requestOptions = {
    method: 'GET',
    redirect: 'follow'
  };

  const response = await fetch('https://xxx.yyy.com/api/zzz/', requestOptions);
  const data = await response.json()
  const response1 = await fetch('https://xxx.yyy.com/api/aaa/'+data.id, requestOptions)
  const data1 = await response1.json()
  console.log(data1)
}

demo().catch(error => console.error('error',error))

The rewritten code is not very clear. There are not so many then to follow, so you don't have to be afraid if there are a series of network requests.

When async is placed before the declaration of a function, the function is an asynchronous function. Calling the function will return a Promise.
await is used to wait for a Promise object, which can only be used in asynchronous functions. await expression will pause the execution of the current asynchronous function and wait for Promise processing to complete.

If you want a series of asynchronous function calls to be executed sequentially, if you call these functions into a async modified function, add await before the call, so that these functions can be executed sequentially.

epilogue

Through the combing of this article, I believe you already know how to avoid callback hell. However, it should be noted that Promise joined the language specification in 2015, while async/await joined the language specification in 2017. If your project is old or must be compatible with the old version of browser (such as IE6) 😂), Then we need to solve the problem in other ways.
For electron, as long as you use the version in recent years, it is supported. Electron can be regarded as chromium and node JS, especially suitable for writing cross platform tool desktop applications.

Keywords: Javascript Front-end Operation & Maintenance

Added by GoodWill on Thu, 03 Mar 2022 04:59:28 +0200