Callbacks hells To Promise / async await rescue
In this article, I would introduce why promise was introduced? What problem wants to be solved when coding in javascript?
I have learned javascript for four to five years, even used it to work for at least three years. However, I still always confused about when to use promise & async/await? Look into the definition, it is the just a asynchronous call [Promise - JavaScript | MDN]. Is it hard to understand? Yes, I think it is not easy.
Callbacks Hells
Let discuss about this topic first. This is not a new thing while writing the javascript. For example, there are few asynchronous task. We want to print in order: task 1 successfully -> task 2 successfully -> task 3 successfully
. However, it is obvious the following example not fulfilled our requirements. How to solve it? Callback!!!
function fetchFirstData1(callback) {
setTimeout(() => {
callback(null, `task 1 successfully`);
}, 3000);
}
function fetchFirstData2(callback) {
setTimeout(() => {
callback(null, `task 2 successfully`);
}, 2000);
}
function fetchFirstData3(callback) {
setTimeout(() => {
callback(null, `task 3 successfully`);
}, 1000);
}
// call in this way cannot get results as our expectations
fetchFirstData1(callback);
fetchFirstData2(callback);
fetchFirstData3(callback);
function callback(error, message) {
if (error) {
console.warn({ error });
} else {
console.log({ message });
}
}
// {message: 'task 3 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 1 successfully'}
Here is the example to use callback to solve the problem. We need to wrap the next call function into callback function. It looks really ugly and difficult to understand. At his time, we definitely want to ask if any other way could make things better and easier? Definitely, that's why promise comes in. To solve the such callback hells and make async call easily understand.
// this is the correct way to get the order as our expectation
// first
fetchFirstData1(function (err, message) {
if (err) {
console.warn({ err });
} else {
console.log({ message });
// second
fetchFirstData2(function (err, message) {
if (err) {
console.warn({ err });
} else {
console.log({ message });
// third
fetchFirstData3(function (err, message) {
if (err) {
console.warn(err);
} else {
console.log({ message });
}
});
}
});
}
});
function fetchFirstData1(callback) {
setTimeout(() => {
callback(null, `task 1 successfully`);
}, 3000);
}
function fetchFirstData2(callback) {
setTimeout(() => {
callback(null, `task 2 successfully`);
}, 2000);
}
function fetchFirstData3(callback) {
setTimeout(() => {
callback(null, `task 3 successfully`);
}, 1000);
}
// {message: 'task 1 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 3 successfully'}
Promise to rescue the callback hell
This is example fully explanation the benefits to use promise to keep our asyn call to escape the callback hell. Only need to execute the next step in the then(). But, wait, do we have any better to make clear? Yes, Async/Await. If you search at the google, you definitely know it is a sugar syntax for promise. Actually, they did the same thing.
// call first
fetchFirstData1()
.then(
(res) => {
callback(null, res);
// call second
return fetchFirstData2();
},
(err) => callback(err)
)
.then(
(res) => {
callback(null, res);
// call third
return fetchFirstData3();
},
(err) => callback(err)
)
.then(
(res) => {
callback(null, res);
},
(err) => callback(err)
);
function fetchFirstData1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`task 1 successfully`);
}, 3000);
});
}
function fetchFirstData2() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`task 2 successfully`);
}, 2000);
});
}
function fetchFirstData3() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`task 3 successfully`);
}, 1000);
});
}
function callback(error, message) {
if (error) {
console.warn({ error });
} else {
console.log({ message });
}
}
// {message: 'task 1 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 3 successfully'}
Advantage of Async / Await
Here, I want to show another way to improve our async calls used in the for loop. This is a nice way for dynamic call async function without write it down one by one.
fetchAll(
[fetchFirstData1, fetchFirstData2, fetchFirstData3],
(error, message) => {
if (error) {
console.warn({ error });
} else {
console.log({ message });
}
}
);
// asyns / await function call
async function fetchAll(tasks, callback) {
for (let task of tasks) {
try {
const res = await task();
callback(null, res);
} catch (error) {
callback(error);
}
}
}
function fetchFirstData1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`task 1 successfully`);
}, 3000);
});
}
function fetchFirstData2() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`task 2 successfully`);
}, 2000);
});
}
function fetchFirstData3() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`task 3 successfully`);
}, 1000);
});
}
// {message: 'task 1 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 3 successfully'}
Conclusion
Promise could solve the callback hells problem. Try to think about any other scenario could be used by promise without callback hells. But, it can't fully replace the callback. Async/Await is just a sugar syntax for Promise.
Thanks for the reading. Welcome to leave the comments to further discussion.
Reference
JavaScript.info - Introduction: callbacks : https://javascript.info/callbacks
JavaScript.info - Promise : https://javascript.info/promise-basics