NodeJS Simple Task Async/Await, Promise Processing
After I dropped from NodeJS for a while, I back to implementing with NodeJS x TypeScript.
I was tested with simple questions of Node JS about how it works, if we have several tasks on a program, does the program run with asynchronous or sequence. ( Sure, Using Single thread and Concept of Event Loop)
There are questions below
- What is the behavior of node and task processing?
- How to handle task processing when the task is blocked by another task?
- How do the concepts of async/await, and promise work?
I’ll make 4 scenarios to handle the result.
From my mistake, I replied not correctly then I made a mistake, I realized had to recover and note simple tasks async/await and promise processing.
I’ll share a Note from the interview question, with other candidates whose has the same curiosity and might respond to these questions.
Let’s try things here in 2 parts.
- The first part prepares NodeJs with TypeScript if you are comfortable with this then skip this part.
- The second part there is only source code.
The first part is to prepare workspace NodeJs with TypeScript
- Go to the directory in which you would like to create the project.
mkdir task-processing
2. Initialize NodeJS with package.json and TypeScript as a dev dependency
npm init -y && npm install typescript --save-dev
{
"name": "task-processing",
"version": "1.0.0",
"description": "poc task-processing",
"main": "src/app.js",
"scripts": {
"build": "tsc",
"start": "node dist/src/app.js",
"prestart": "npm run build",
"dev": "nodemon app.ts"
},
"keywords": [],
"author": "aoftionstyle",
"license": "ISC",
"devDependencies": {
"typescript": "^5.3.3"
}
}
3. Create directory src/app.ts
4. Add TypeScript configuration
npx tsc --init
// tsconfig.json
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true , /* Skip type checking all .d.ts files. */
}
}
5. VS Code editor go to debugger mode, create a launch.json file
A file will be created in the directory .vscode/lauch.json
// launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "npm: build",
"sourceMaps": true,
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"program": "${workspaceFolder}/dist/app.js",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
The second part is the idea of how to get to know async/await and promise, I have an idea to represent 4 scenarios and more tips I found a good presentation of the event loop animated below this blog.
- Create a directory and create TypeScript file src/app.ts
- Bring code to app.ts
// app.ts
console.log("Hello Task Processing");
(async () => {
console.log("=======================================")
await scenario1();
console.log("=======================================")
await scenario2();
console.log("=======================================")
await scenario3a();
console.log("=======================================")
await scenario3b();
console.log("=======================================")
await scenario4();
})();
async function scenario4() {
// hybrid simple function + promise function
console.log("==== start scenario4 ==== when task2 simple function process before task1, task3 by await result after call fn()");
const t1 = task("task1", 0.2)
const t2 = (() => {
const name = "task2";
const second = 3;
for(let i = 0; i < second; i++) {
console.log(`Delayed ${name} for 0.${i} second.`);
}
return second;
})();
const t3 = task("task3", 0.1);
const [result1, result2, result3] = [await t1, t2, await t3];
console.info(`t1:${result1} + t3:${result3} = ${result1 + result3}`);
}
async function scenario3b() {
console.log("==== start scenario3-b ====\n" +
"when task2 process longer than task1, task3 by using Promise.all to execute multiple tasks without having to wait for another task to finish.\n" +
"using .then callback until done both tasks from await");
const t1 = task("task1", 0.2);
const t2 = task("task2", 0.3);
const t3 = task("task3", 0.1);
const [result1, result2, result3] = await Promise.all([t1, t2, t3]).then(([val1, val2, val3]) => {
return [val1, val2, val3];
});
console.info(`t1:${result1} + t3:${result3} = ${result1 + result3}`);
}
async function scenario3a() {
console.log("==== start scenario3-a ====\n" +
"when task2 process longer than task1, task3 by using .then callback until done each task from await");
const t1 = task("task1", 0.2);
const t2 = task("task2", 0.3);
const t3 = task("task3", 0.1);
const [result1, result2, result3] = [
await t1.then((val) => { return val; }),
await t2.then((val) => { return val; }),
await t3.then((val) => { return val; })
];
console.info(`t1:${result1} + t3:${result3} = ${result1 + result3}`);
}
async function scenario2() {
console.log("==== start scenario2 ==== when task2 process longer than task1, task3 by await result after call fn()");
const t1 = task("task1", 0.2);
const t2 = task("task2", 0.3);
const t3 = task("task3", 0.1);
const [result1, result2, result3] = [await t1, await t2, await t3]; // tuple
console.info(`t1:${result1} + t3:${result3} = ${result1 + result3}`);
}
async function scenario1() {
console.log("==== start scenario1 ==== when task1, task2 process sequentially by await on call fn()");
const t1 = await task("task1", 0.2);
const t2 = await task("task2", 0.3);
const t3 = await task("task3", 0.1);
console.info(`t1:${t1} + t3:${t3} = ${t1 + t3}`);
}
function task(name: string, second: number): Promise<number> {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Delayed ${name} for ${second} second.`);
resolve(second);
}, second * 1000);
});
}
Remark: The global setTimeout() method sets a timer which the timeout will continue to execute separately and the call stack will continue to execute other statements asynchronously.
The Event Loop Animated
https://dev.to/nodedoctors/an-animated-guide-to-nodejs-event-loop-3g62