Fri Mar 05 2021
JavaScript is a highly flexible and convenient asynchronous language. I like the JavaScript ecosystem very much, and I often use ReactJS, Node.js, etc. (Recently, I've been studying to transfer to Go.Hmmm) The most inconvenient thing about JavaScript, a really convenient and good language, is Callback Hell. I tried to synchronize JavaScript, which is an asynchronous language, but there was a callback in the callback, a callback in the callback... These Callback hells are not only fatal in collaboration, but also in a way that reduces code readability. If you look at even the code you made two weeks later, you will have to interpret it. The solution to these problems is Promise, and nevertheless, as code readability has begun to decline, the processing of using async/await together has become a trend.
Promise is the first way to solve JavaScript's Callback Hell. If the code is written inside Promise and works normally, resolve, reject can be specified by the code author, and .then().catch().Finally, the result value can be processed with the result value can be processed.
Note
A promise is said to be settled if it is either fulfilled or rejected, but not pending.
After 2 seconds, create a random number from 0 to 9, and write a code using Promise that returns an even sleep value and returns an odd sleep error.
const isRandomNumberEven = new Promise((resolve, reject) => {
setTimeout(() => {
let random = Math.floor(Math.random() * 10);
if (random % 2 === 0) {
resolve(random);
} else {
reject(new Error("The random number is odd"));
}
}, 2000);
});
isRandomNumberEven
.then(random => {
// even
console.log(`random number is ${random}`);
})
.catch(error => {
// odd
console.log(error);
})
.finally(() => {
// whatever
console.log(
"The handler is called when the promise is settled, whether fulfilled or rejected."
);
});
One thing to be careful about using Promise is that the Promise internal code runs as soon as Promise is created. In the code above, the internal code for Promise is isRandomNumberEven.then()... It does not run on part, but directly on part new Promise()... In the isRandomNumberEven.then()..., it only has the result of the internal code of Promise working. You should always think about this and be careful not to needlessly operate Promise.
With Promise above, I thought I could clean up the code that works asynchronously, but... Promise has been used so much in code that it is less readable. Solving Callback Hell, Promise Hell unfolds. So the second solution to solve the Callback hell is async/await. It should be noted that awit is only available inside the async function. It's really simple to use.
First, the use of the async function. All you have to do is put async before the function declaration. Subsequently, the processing method is same as Promise.then().catch().finally().
async function func() {
return 1;
}
func().then(alert); // 1
const fun = async () => {
return 2;
};
fun().then(alert); // 2
A code that waits for the value of Promise to be returned within the async function.
function resolveAfter2Seconds() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function () {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
}
function resolveAfter1Second() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function () {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
}
In the sequentialStart() function below, both slow and fast are handled synchronously, which takes three seconds to produce all the values.
async function sequentialStart() {
console.log("==SEQUENTIAL START==");
// 1. Execution gets here almost instantly
const slow = await resolveAfter2Seconds();
console.log(slow); // 2. this runs 2 seconds after 1.
const fast = await resolveAfter1Second();
console.log(fast); // 3. this runs 3 seconds after 1.
}
sequentialStart();
// after 2 seconds, logs "slow", then after 1 more second, "fast"
In the currentStart() function below, slow and fast are executed asynchronously, but await slow is executed first, so fast is output after the run is completed.
async function concurrentStart() {
console.log("==CONCURRENT START with await==");
const slow = resolveAfter2Seconds(); // starts timer immediately
const fast = resolveAfter1Second(); // starts timer immediately
// 1. Execution gets here almost instantly
console.log(await slow); // 2. this runs 2 seconds after 1.
console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}
concurrentStart();
// after 2 seconds, logs "slow" and then "fast"
The following function concurrentPromise() uses the Promise.all(iterable) method, which was identified in 2-2.1 above, to execute Promises asynchronously. All results are returned after the longest of the Promises is terminated.
function concurrentPromise() {
console.log("==CONCURRENT START with Promise.all==");
return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then(
messages => {
console.log(messages[0]); // slow
console.log(messages[1]); // fast
}
);
}
concurrentPromise();
// same as concurrentStart
The paraller function below synchronously handled the longest termination of Promise after returning the return value of each Promise asynchronously using await.
async function parallel() {
console.log("==PARALLEL with await Promise.all==");
// Start 2 "jobs" in parallel and wait for both of them to complete
await Promise.all([
(async () => console.log(await resolveAfter2Seconds()))(),
(async () => console.log(await resolveAfter1Second()))(),
]);
}
parallel();
// truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"
For conventional Promise, exception handle was carried out in the .catch() chain, but exception processing can be carried out in async/await as a try-catch-finally syntax.
// Promise
function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch(e => {
return downloadFallbackData(url); // returns a promise
})
.then(v => {
return processDataInWorker(v); // returns a promise
});
}
// async/await
async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch (e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}