How to Solve Callback Hell with Promises

1. What is Callback Hell


The code of asynchronous tasks cannot be guaranteed to be executed in order. If we need the code to be executed in sequence, how should we write it?
setTimeout(function () { //The first layer
console.log(111);
setTimeout(function () { //Second pass
console.log(222);
setTimeout(function () { //The third layer
console.log(333);
}, 1000)
}, 2000)
}, 3000)

This nesting of callback functions is called callback hell. Callback hell will cause problems such as poor code reusability, poor readability, maintainability (poor iteration), and poor scalability.
So how to solve callback hell?

2. Solution


2.1 Promise syntax
Promise, the Chinese translation is 'promise', which means to promise to return data to you at a certain point in the future. It is a native object in js and an asynchronous programming solution that can replace the traditional callback function solution.
The essence of promise is not to control the execution order of asynchronous code (which cannot be controlled), but to control the order of asynchronous code result processing
The promise itself is just a container, the real asynchronous is its two callbacks resolve() and reject()


The promise object has three states: pending (in progress), fulfilled (successful), rejected (failed)


The state of the Promise object changes, there are only two possibilities:
a. From pending to fulfilled
* resolve(); should be executed at this point
b. Change from pending to rejected.
* should execute reject();


When a promise is created, the code in it will be executed immediately.
a. When promise is created, the code inside is still asynchronous and out-of-order operation
b. The principle of promise is to use the then method to execute the results of the asynchronous operation in order, and the catch method is used to receive the corresponding data when the processing fails
*** Summary: Don't handle asynchronous operation results when creating promises, but handle them through the then() method ***


Promise resolution callback hell principle: return a promise object in the then method
*** In the then method of the previous promise, return the next promise ***


//1. Encapsulate a function: generate the promise read from the file according to the file name
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//read file
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//success
resolve(data);
} else {
//fail
reject(err);
}
});
});
return p;
};

//2. Solve the requirements: read a first, read b after reading a, read c after reading b.

//start reading a
getPromise('a').then((data)=>{
console.log(data);
// continue to read b
return getPromise('b');
}).then((data)=>{
console.log(data);
// continue to read c
return getPromise('c');
}).then((data)=>{
console.log(data);
}).catch((err)=>{
//The above three asynchronous operations, as long as any one of them fails, err will be executed
console.log(err);
});



But if you use then too much, it will also cause a new execution flow problem. So ES2017 introduced async/await (asynchronous wait)


2.2 async/await


In one sentence: async function is equivalent to another advanced writing method of promise asynchronous function


The async syntax is as follows




Use async modifier in front of the function


Inside the function, the promise operation is decorated with await


- `await` is followed by `promise object`, and the return value on the left is the result of the then method of this promise object
- `await` must be written in the function modified by `async` and cannot be used alone, otherwise the program will report an error



Exceptions inside async functions can be caught by .catch() or try/catch, the difference is that try/catch can catch all exceptions, and .catch() can only catch reject errors in asynchronous methods

const fs = require("fs");

/*
The catch method of the promise instance object: used to capture the error information of the asynchronous operation
*/


//1. Encapsulate a function: generate the promise read from the file according to the file name
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//read file
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//success
resolve(data);
} else {
//fail
reject(err);
}
});
});
return p;
};

//2. Solve the requirements: read a first, read b after reading a, read c after reading b.

// async and await asynchronous functions: These two keywords can only be used in functions, so they must be used in functions when they are used.

/*
async keyword: decorated function. Indicates that there is an asynchronous operation inside this function.
await keyword: Wait for the asynchronous execution to complete.
(1) await can only be used in functions modified by async.
Only when the asynchronous operation behind await is completed, will the following code continue to be executed
(2) The back of await can only be a promise object
*/

const readFile = async() => {

let data1 = await getPromise('a')
console.log(data1)

let data2 = await getPromise('b')
console.log(data2)
}

readFile()

3. async function error capture
async function getCatch () {
await new Promise(function (resolve, reject) {
reject(new Error('fail'))
}).catch(error => {
console.log(error) // .catch() can only catch reject errors in async methods
})
}

async function getCatch () {
try {
throw new Error('11')
await new Promise(function (resolve, reject) {
reject(new Error('fail'))
})
} catch (error) {
console.log(error) // try/catch catches all exceptions
}
}

3. Summary
When we write code and encounter asynchronous callbacks, we want the asynchronous code to be executed in the order we want. If we follow the traditional nesting method, there will be callback hell, which is not conducive to maintenance. We can solve it by chaining Promise objects, so although it can solve the problem, ES7 provides us with more comfortable async/await syntactic sugar.

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00