7
头图
Abstract
This article introduces the writing methods of three asynchronous network requests based on XMLHttpRequest, Promise, and async/await. The async/await writing method allows us to write asynchronous programs in a synchronous manner, and get rid of cumbersome callback functions.

1. Background

In order to cope with more and more testing requirements and reduce repetitive work, Youdao intelligent hardware testing team has developed a series of test efficiency tools based on electron.

The programming language of electron is js, because everyone is not a professional front-end, and they are not familiar with js, so they have stepped on a lot of pits when writing programs. Especially events and network requests in js, where asynchronous programming is involved are prone to errors.

With the rapid development and iteration of the tool, more and more nested callback functions appear in the code, and the probability of the tool crashing is also increasing. In order to solve these problems, We refactored these callback functions with async/await , which reduced the amount of code and greatly improved the readability and understandability of the code.

This article introduces the writing method of based on XMLHttpRequest, Promise, async/await and other three asynchronous network requests. The async/await writing method allows us to write asynchronous programs in a synchronous manner and get rid of cumbersome callback functions.

2. Preface

In js, it is not too complicated to initiate a single network request. Using fetch, axios or directly using XMLHttpRequest can meet the requirements.

But if multiple requests pull data in sequence, it will be very troublesome to write, because the network requests in js are asynchronous, and the most common way to execute sequentially is to initiate the next request in the callback function, as follows Face these 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 obtain a data in two steps, such as obtaining a data object data from https://xxx.yyy.com/api/zzz /, and get the serial number of the data I want to obtain through data.id, and then Send a request to get the desired data.

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

Next, I will sort out several network request methods of js to get rid of the callback hell, hoping to help friends who encounter similar problems.

(1) XMLHttpRequest

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

// 假设访问http://localhost:3000/user返回json对象{"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, then adds a callback function for the readystatechange event to xhr.onreadystatechange, then xhr.open('GET', url) initializes the request, and finally sends the request by xhr.send().

After the request is sent, the program will continue to execute without blocking, which is also the benefit of asynchronous calls. When the browser receives the response, it will enter the callback function of xhr.onreadystatechange. In the whole request process, xhr.onreadystatechange will be triggered four times, and the readyState will be incremented each time, from 1 to 4. The final response data can only be obtained when the readyState is 4 in the final stage. After reaching the fourth stage, it is necessary to judge whether the status code of the response is normal according to the status. Usually, the response code is 200, indicating that the request has not encountered any problems. This code will eventually print YouDao on the console.

It can be seen that when processing requests through XMLHttpRequest, firstly, an XMLHttpRequest object must be created for each request, and then the callback function of the readystatechange event must be bound to each object. If multiple requests are chained together, it is very troublesome to think about.

(2) Promises

Promises were introduced in ECMAScript 2015, and using callbacks can complicate the code if one event depends on the result returned by another event. Promise objects provide a pattern for checking for failure or success of an operation. If successful, another Promise is returned. This makes the writing of callbacks more standardized.

The routine of through 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 string together the whole process. First, a Promise object is created, and 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 to be executed after an error. The executed function reject.

resolve refers to the callback function in then after successful execution, and reject refers to the callback function executed in catch after execution fails. The final finally is executed regardless of success or failure, and can be used to do some finishing touches.

Promise-based network requests can be implemented using the axios library or the fetch that comes with the browser.

The routine for creating a request for the axios library 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 require library import. The way fetch creates requests is similar to that of axios. It has been shown at the beginning and will not be repeated.

Although Promise simplifies the way of writing callback functions, it still does not get rid of callback hell. If multiple requests are strung together, a new Promise will be created in then as I wrote at the beginning, which will eventually become Promise hell.

(3) async/await

async/await was introduced in ECMAScript 2017, which can simplify the writing of Promises, so that asynchronous function calls in the code can be executed in sequence, which is 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 very clear, there are not so many thens behind, so if there is a series of network requests, there is no need to be afraid.

When async is placed before the declaration of a function, the function is an asynchronous function, and calling the function returns a Promise.
await is used to wait for a Promise object, which can only be used in asynchronous functions. The await expression suspends the execution of the current asynchronous function and waits for the completion of the Promise processing.

In this way, if you want a series of asynchronous function calls to be executed sequentially, you only need to put these functions to be called into a function decorated with async, and add await before the call to make these functions execute in sequence obediently.

Epilogue

Through the sorting out of this article, I believe you already know how to avoid callback hell. However, it should be noted that Promise was added to the language specification in 2015, while async/await was added to the language specification in 2017. If your project is relatively old or must be compatible with older browsers (such as IE6😂), Then you need to solve the callback hell in another way.
For electron, as long as you use the version in recent years, it is supported. Electron can be regarded as a combination of chromium and node.js, which is especially suitable for writing cross-platform tool desktop applications.


有道AI情报局
788 声望7.9k 粉丝