Home > Back-end >  Node Async practical benefit?
Node Async practical benefit?

Time:04-10

I am new to node async/sync concept. these day i am confused by the sequence of async function executions. I am kinda hard to get the benefit of the async calls of single-process node:

Here is an example:

1.Sequence of async/await

Assume an async function:
async readPathInFile(filePath) {
     //open filePath, read the content and return 
     return file path in filePath
}

We have several calls like:

var a = 'a.txt';
var b = await readPathInFile(a); // call_a
var c = await readPathInFile(b); // call_b
var d = await readPathInFile(c); // call_c

As we know the readPathInFile is defined as asynchronous, but the calls call_a, call_b, call_c relies on the previous ones sequentially.

For these calls, the calls have no differences with synchronous calls.

So, What is the benefit of the asynchronous definition ?

  1. Callbacks concept has the same concerns:

For example:

readPathInFile(filePath, callback) {
     //open filePath, read the content and return 
     callback(file path in filePath)
}
var a = 'a.txt';
readPathInFile(a, (b)=>{
    //call_a
    readPathInFile(b, (c)=>{
        //call_b
        readPathInFile(c, (d)=>{
        //
        });
    });
}); // call_a
call_z(); 

//only if the call_z does not rely on a,b,c, z finishes without caring about a,b,c. This is a only benefit.

Please correct me if my assumption is wrong.

CodePudding user response:

As the term implies, asynchronous means that functions run out of order. In this respect your understanding is correct. What this implies however is not at all obvious.

To answer your question is a few words, the async/await keywords are syntax sugar, meaning they're not so much intrinsic qualities of the language, rather are shortcuts for saying something else. Specifically, the await keyword translates the expression into the equivalent Promise syntax.

function someFunction(): Promise<string> {
  return new Promise(function(resolve) {
    resolve('Hello World');
  });
}

console.log('Step 1');
console.log(await someFunction());
console.log('Step 3');

is the same as saying

...
console.log('Step 1');
someFunction().then(function (res) {
  console.log(res);
  console.log('Step 3');
});

Note the 3rd console.log is inside the then callback.

When you redefine someFunction using the async syntax, you'd get the following:

async function someFunction() {
  return 'Hello World';
}

The two are equivalent.

To answer your question

I'd like to reiterate that async/await are syntactical sugar. their effect can be replicated by existing functionality

Their purpose therefore is to combine the worlds of asynchronous and synchronous code. If you've ever seen an older, larger JS file, you'll quickly notice how many callbacks are used throughout the script. await is the mechanism to use asynchronous functions while avoiding the callback hell, but fundamentally, a function marked async will always be async regardless of how many awaits you put in front of it.

If you're struggling to understand how an await call works, just remember that the callback you pass to .then(...) can be called at any time. This is the essence of asynchronicity. await just adds a clean way to use it.

CodePudding user response:

What is the benefit of the asynchronous definition ?

To enable standard logical-flow code to run asynchronously. Contrast your examples:

var a = 'a.txt';
var b = await readPathInFile(a); // call_a
var c = await readPathInFile(b); // call_b
var d = await readPathInFile(c); // call_c

vs.

var a = 'a.txt';
readPathInFile(a, (b)=>{
    //call_a
    readPathInFile(b, (c)=>{
        //call_b
        readPathInFile(c, (d)=>{
        //
        });
    });
}); // call_a

The latter is often called "callback hell." It's much harder to reason about even in that case, but as soon as you add branching and such, it gets extremely hard to reason about. In contrast, branching with async functions and await is just like branching in synchronous code.

Now, you could write readPathInFileSync (a synchronous version of readPathInFile), which would not be an async function and would let you do this:

var a = 'a.txt';
var b = readPathInFileSync(a); // call_a
var c = readPathInFileSync(b); // call_b
var d = readPathInFileSync(c); // call_c

So what's the difference between that and the async version at the beginning of the answer? The async version lets other things happen on the one main JavaScript thread while waiting for those I/O calls to complete. The synchronous version doesn't, it blocks the main thread, waiting for I/O to complete, preventing anything else from being handled.

Here's an example, using setTimeout to stand in for I/O:

const logElement = document.getElementById("log");
function log(msg) {
    const entry = document.createElement("pre");
    entry.textContent = Date.now()   ": "   msg;
    logElement.appendChild(entry);
    entry.scrollIntoView();
}

// Set up "other things" to happen
const things = [
    "this", "that", "the other", "something else", "yet another thing", "yada yada"
];
let starting = false; // We need this flag so we can show
                      // the "Doing X work" messsage before
                      // we actually start doing it, without
                      // giving the false impression in
                      // rare cases that other things happened
                      // during sync work
otherThing();
function otherThing() {
    if (!starting) {
        log(things[Math.floor(Math.random() * things.length)]);
    }
    setTimeout(otherThing, 250);
}

// Stand-in for a synchronous task that takes n milliseconds
function doSomethingSync(n) {
    const done = Date.now()   n;
    while (Date.now() < done) {
        // Busy-wait. NEVER DO THIS IN REAL CODE,
        // THIS IS SIMULATING BLOCKING I/O.
    }
}

// Stand-in for an asynchronous task that takes n milliseconds
function doSomething(n) {
    return new Promise(resolve => {
        setTimeout(resolve, n);
    });
}

function doWorkSync() {
    doSomethingSync(200);
    doSomethingSync(150);
    doSomethingSync(50);
    doSomethingSync(400);
}

async function doWorkAsync() {
    await doSomething(200);
    await doSomething(150);
    await doSomething(50);
    await doSomething(400);
}

// Do the work synchronously
document.getElementById("run-sync").addEventListener("click", (e) => {
    const btn = e.currentTarget;
    btn.disabled = true;
    log(">>>> Doing sync work");
    starting = true;
    setTimeout(() => {
        starting = false;
        doWorkSync();
        log("<<<< Done with sync work");
        btn.disabled = false;
    }, 50);
});

// Do the work asynchronously
document.getElementById("run-async").addEventListener("click", (e) => {
    const btn = e.currentTarget;
    btn.disabled = true;
    log(">>>> Doing async work");
    starting = true;
    setTimeout(() => {
        starting = false;
        doWorkAsync().then(() => {
            log("<<<< Done with async work");
            btn.disabled = false;
        });
    }, 50);
});
html {
    font-family: sans-serif;
}
#log {
    max-height: 5em;
    min-height: 5em;
    overflow: auto;
    border: 1px solid grey;
}
#log * {
    margin: 0;
    padding: 0;
}
<div>Things happening:</div>
<div id="log"></div>
<input type="button" id="run-sync" value="Run Sync">
<input type="button" id="run-async" value="Run Async">

There are times you don't care about that blocking, which is what the xyzSync functions in the Node.js API are for. For instance, if you're writing a shell script and you just need to do things in order, it's perfectly reasonable to write synchronous code to do that.

In constrast, if you're running a Node.js-based server of any kind, it's crucial not to allow the one main JavaScript thread to be blocked on I/O, unable to handle any other requests that are coming in.

So, in summary:

  • It's fine to write synchronous I/O code if you don't care about the JavaScript thread doing other things (for instance, a shell script).
  • If you do care about blocking the JavaScript thread, writing code with async/await makes it much easier (vs. callback functions) to avoid doing that with simple, straight-forward code following the logic of your operation rather than its temporality.

CodePudding user response:

Javascript Promises lets asynchronous functions return values like synchronous methods instead of immediately returning the final value the asynchronous function returns a promise to provide data in the future.

Actually Promises solves the callback hell issue.

  • Related