NodeJS Simple Task Async/Await, Promise Processing

Chutipon Pongpanit (Aof)
5 min readFeb 28, 2024

--

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

  1. 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

create a luanch.json

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.

  1. Create a directory and create TypeScript file src/app.ts
  2. 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

present code of call stack and event queue
call stack console.log
call stack and waiting result in LIBUV API
call stack next console.log
result from LIBUV move to event queue, then event loop move call stack

--

--

Chutipon Pongpanit (Aof)

Aoftionstyle idea here #idea is anything happen faster speed of light then captured in the chest. no ✔ no ✘ just Casually open chest beacause world need you