Vish
Vish

Reputation: 21

How to write custom async function in Node.js

I am new to javascript, trying to understand async function invocation. Here is my js program:

var notify =  function () {
    console.log('before');
    doPost('data', (res) => {
        console.log('callback received');
    });
    console.log('after');
}
var doPost = function (data) {
    console.log(data);
    while (true) {
        //do nothing
    }    
}
notify();

If my understanding is correct, notify()-->doPost() is async manner, therefore 'after' should be printed immediately after 'before'. But that does not happen. My program wait infinite loop to complete first. I know there is some loophole in my understanding. Please help.

I tried these below two programs: This one again shows sync behavior. Prints:- before data

 var notify = function () {
    console.log('before');
    doPost('data').then((res) => {
        console.log('callback received');
    });
    console.log('after');

}
var doPost = function (data) {
    console.log(data);
    return new Promise((resolve, reject) => {
        resolve('resolved');
        while (true) {
            //do nothing           
        }
    });
};
notify();

However if I just remove the infinitely running while loop, it start showing async behaviour. Prints:- before data after callback received

var notify = function () {
    console.log('before');
    doPost('data').then((res) => {
        console.log('callback received');
    });
    console.log('after');

}
var doPost = function (data) {
    console.log(data);
    return new Promise((resolve, reject) => {
        resolve('resolved');
        while (false) {
            //do nothing           
        }

    });
};
notify();

This is beyond my understanding. Any help would be highly appreciated.

Upvotes: 2

Views: 1168

Answers (3)

marzelin
marzelin

Reputation: 11600

Looking at your examples you seem to think of async as parallel execution since you expect that the code in the main thread keeps executing while an infinite loop is run on another thread. Parallel execution is a form of asynchronous operation but in javascript we generally use a concurrent execution.

But what does sync, async, parallel, concurrent really mean?

Synchronous code is the easiest to work with because it gives best guarantees:

/* 1 */ doWorkSync(() => console.log("callback"));
/* 2 */ console.log("after doWorkSync");

In the above code where doWorkSync is a synchronous function we know that the callback function will be executed before line 2.

Concurrent code that in javascript is commonly referred to as async gives us a slightly worse guarantee:

/* 1 */ doWorkAsync(() => console.log("callback"));
/* 2 */ console.log("after doWorkAsync");

In the code above we are guaranteed that the callback function will be called after the current synchronous code will be completely executed, that is after line 2. But we don't know exactly when. It may be called immediately but it may be called a lot later.

But the most difficult code to work with is parallel code:

/* 1 */ doWorkParallel(() => console.log("callback"));
/* 2 */ console.log("after doWorkParallel");

In the code above we have no guarantees. The callback code can be run before line 2, at the same time or later. So the code have to be written in a way that it's prepared for any of those situations.

The easiest way to write an asynchronous (concurrent) function in JavaScript is to use Promise:

console.log("first");
Promise.resolve().then(() => console.log("last"));
console.log("second");

In the code above then callback is executed immediately after the current synchronous code is run from the microtask queue.

Parallel code can be executed in JavaScript with Workers:

function parallel() {

  function work() {
    block(200); // message from the main thread is logged while this blocks the worker
    console.log("second"); // logs from the worker can be seen in the browser console

    function block(time) {
      const startDate = Date.now();
      while (startDate + time > Date.now()) {};
    }
  }

  new Worker(window.URL.createObjectURL(new Blob([`(${work})()`]), {
    type: 'application/javascript'
  }));

}

function block(time) {
  const startDate = Date.now();
  while (startDate + time > Date.now()) {}
}

parallel();

// timeout is needed because blob response is handled on the main thread
setTimeout(() => { 
  console.log("first");
  block(500); //  message from parallel is logged while this blocks the main thread
  console.log("last");
}, 100)

Upvotes: 2

bdifferent
bdifferent

Reputation: 703

Why do you want to write your own async functions? There are so many different utilities already available:

Look at the examples here. Wont that help? https://caolan.github.io/async/

Upvotes: 1

Jonas Wilms
Jonas Wilms

Reputation: 138235

Using callbacks or promises does not necessarily mean that something is asynchronous. Take this example:

   console.log("before");
   [1, 2, 3].forEach(console.log);
   console.log("after");

That will log everything in order, as .forEach calls the callback synchronously. However, there are also asynchrnous callbacks, setTimeout for example:

 console.log("before");
 setTimeout(function later() {
   console.log("later");
  }, 0);
  console.log("after");

Here you will see "before", "after", "later", as the later function gets executed asynchronously when the timer is done. To get asynchronous behaviour, you need an "asynchronous root callback", all callbacks / promises that are based on something asynchronous are also asynchronous. In Javascript there are just a few such "asynchronous roots", namely setTimeout and setInterval, fetch and the DOM event handlers on browsers or all the IO on nodejs. In your code there is none of these, therefore it is, no matter what you do, completely synchronous.

To make it behave asynchronous, add an asynchronous callback:

function notify() { // function declarations look so much better than function expressions, and they are easier to handle
  console.log('before');
  doPost('data', (res) => {
    console.log('callback received');
  });
  console.log('after');
}

function doPost(data, callback) { // you pass a callback, so you should take it here
  console.log(data);
  setTimeout(callback, 1, "result"); // call back asynchronously
}

notify();

Oh and while(true) is usually an indicator that you made something very wrong.

Upvotes: 2

Related Questions