preface

This article shares a few simple but crude solutions to callback hell for learning purposes

The callback hell

Let’s start with an example that is constantly nested and indented, making code hard to read and hard to maintain.

const xhr1 = new XMLHttpRequest();
xhr1.open("GET".'http://127.0.0.1:5500/index.html');
xhr1.send();
xhr1.onreadystatechange = function () {
    if (xhr1.readyState === 4) {
        if (xhr1.status >= 200 && xhr1.status < 300) {
            console.log(`success1: ${xhr1.response}`)

            const xhr2 = new XMLHttpRequest();
            xhr2.open("GET".'http://127.0.0.1:5500/index2.html');
            xhr2.send();
            xhr2.onreadystatechange = function () {
                if (xhr2.readyState === 4) {
                    if (xhr2.status >= 200 && xhr2.status < 300) {
                        console.log(`success2: ${xhr2.response}`)

                        const xhr3 = new XMLHttpRequest();
                        xhr3.open("GET".'http://127.0.0.1:5500/index3.html');
                        xhr3.send();
                        xhr3.onreadystatechange = function () {
                            if (xhr3.readyState === 4) {
                                if (xhr3.status >= 200 && xhr3.status < 300) {
                                    console.log(`success3: ${xhr3.response}`)}else {
                                    console.log(`error3: ${xhr3.status}`); }}}; }else {
                        console.log(`error2: ${xhr2.status}`); }}}; }else {
            console.log(`error1: ${xhr1.status}`); }}};Copy the code

The callback problem is solved by extracting functions

We can extract functions to solve the callback problem, and we optimize the code above

function request1 () {
  const xhr = new XMLHttpRequest();
  xhr.open("GET".'http://127.0.0.1:5500/index.html');
  xhr.send();
  xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
              console.log(`success1: ${xhr.response}`)

              request2(xhr.response);
          } else {
              console.log(`error1: ${xhr.status}`); }}}; }function request2 (response) {
  const xhr = new XMLHttpRequest();
  xhr.open("GET".'http://127.0.0.1:5500/index2.html');
  xhr.send();
  xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
              console.log(`success1: ${xhr.response}`)

              request3(xhr.response);
          } else {
              console.log(`error1: ${xhr.status}`); }}}; }function request3 (response) {
  const xhr = new XMLHttpRequest();
  xhr.open("GET".'http://127.0.0.1:5500/index3.html');
  xhr.send();
  xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
              console.log(`success1: ${xhr.response}`)}else {
              console.log(`error1: ${xhr.status}`); }}}; } request1();Copy the code

throughPromiseSolve callback hell

Basic usage

Defined in MDN: Promise objects are used to represent the final completion (or failure) of an asynchronous operation and its resulting value.

The Promise A + specification and its translation

In simple terms, we can use the Promise class to instantiate the Promise object for asynchronous operation. The Promise object has three states. The initial state is PENDING, the successful state is RESOLVED, and then is automatically called. Reject is changed to REJECTED, and catch is automatically called. Once the status changes, it does not change again

Note that the callback function itself when instantiating a Promise is synchronous; only the logic in then and catch is asynchronous

example

const p = new Promise((resolve, reject) = > {
  const xhr = new XMLHttpRequest();
  // I used the Live Server plug-in and requested this document here
  xhr.open("GET"."http://127.0.0.1:5500/index.html");
  xhr.send();
  xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
              resolve(xhr.response);
          } else{ reject(xhr.status); }}}; }); p.then((res) = > {
  console.log('then: ' + res);
}).catch((err) = > {
  console.log('catch: ' + err);
});
Copy the code

Chain calls

Since then and catch return promise objects, we can make chain calls, which can easily solve the problem of callback hell.

// There is too much duplicate code. Here we write a function to generate Promise objects
function createPromise(url) {
  return new Promise((resolve, reject) = > {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.send();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                console.log(`resolve:${url}`)
                resolve(xhr.response);
            } else {
                console.log(`reject:${url}`) reject(xhr.status); }}}; })}const p1 = createPromise("http://127.0.0.1:5500/index.html")
const p2 = createPromise("http://127.0.0.1:5500/index2.html")
const p3 = createPromise("http://127.0.0.1:5500/index3.html")

const await1 = p1.then((res) = > {
  console.log('then1: ' + res);
  return p2;
}).then((res) = > {
  console.log('then2: ' + res);
  return p3;
}).then((res) = > {
  console.log('then3: ' + res);
}).catch((err) = > {
  console.log('catch: ' + err);
})
Copy the code

throughsyncawaitSolve callback hell

Async and await syntactic candy of promise

Async is used to declare that a function is asynchronous and the return value of async function is a Promise object

Await must be used in async functions and will block subsequent asynchronous statements until the result of the expression is returned, and then if it is a promise it will become resolved and return its value as the result of the operation.

With async and await we turn async into synchronization, but at the same time there is a problem, the return value of async is a promise object, so we need to use await operator when we get the return value of async function, which causes async hell.

async function asyncFun1 () {
  return 1;
}
console.log(asyncFun1()) // Promise {<fulfilled>: 1}

async function asyncFun2 () {
  console.log('asyncFun2: ' + 1)
  try {
      const awaitResult1 = await p1;
      console.log('asyncFun2: ' + awaitResult1)
  } catch (e) {
      console.log('asyncFun2: ' + e);
  }
  // The string is equivalent to the RESOLVED Promise object
  const awaitResult2 = await 'Hello World';
  console.log('asyncFun2: ' + awaitResult2)
  console.log('asyncFun2: ' + 2)
  return awaitResult2;
}

async function callAsyncFun () {
  const result = await asyncFun2();
  console.log('callAsyncFun: ' + result);
}

callAsyncFun()
Copy the code