Anirban Acharya Anirban Acharya - 3 months ago 6
Javascript Question

I am trying to write a simple asynchronous code in JS, but it doesn't seem to work



function order(number) {
console.log("Queuing order: " + number);
for (var i = 0; i < 1000000000; i++); // kill time
console.log("Order: " + number + " completed");
}

function takeOrder(number, cb) {
console.log("Preparing order: " + number + "");
}
console.log("Starting to accept order");
for (var i = 0; i < 3; i++) {
console.log("Taking order: " + i);
takeOrder(i, order(i));
}
console.log("Job completed!");




I am trying to make it work as while the order(number) is busy executing, the program continues to display the message "Preparing order: .." and "Taking order: .."
Where am I going wrong?

Answer

I am trying to write a simple asynchronous code in JS...

Nothing in your code is asynchronous. Also, in this code

takeOrder(i, order(i));

you're calling order and passing in i, then passing its return value (undefined) into takeOrder. To pass order into takeOrder instead, remove the (i):

takeOrder(i, order);

I am trying to make it work as while the order(number) is busy executing, the program continues to display the message "Preparing order: .." and "Taking order: .."

JavaScript on browsers is run on a single main thread with access to the UI, and then zero or more web worker threads that you create. If the for loop in your code is running on the main UI thread, nothing else can happen while it's running, because of JavaScripts run-to-completion semantics.

If the work represented by the for loop is asynchronous, you don't have to do anything, it's just that your simulation using a for loop wasn't an accurate model of your real work. For instance, if it's doing an ajax request.

If the work represented by the for loop is completely synchronous like your for loop is, I'd probably offload that for loop to a web worker:

worker.js:

self.addEventListener("message", function(e) {
  if (e.data && e.data.command == "go") {
    for (var i = 0; i < 1000000000; i++); // kill time
    self.postMessage({command: "log", message: "Order: " + e.data.order + " completed"});
  }
});

Your main JS file:

function order(number) {
  var w = new Worker("worker.js");
  w.addEventListener("message", function(e) {
    if (e.data && e.data.command == "log") {
      console.log(e.data.message);
    }
    w = null;
  });
  console.log("Queuing order: " + number);
  w.postMessage({command: "go", order: number});
}

function takeOrder(number, cb) {
  console.log("Preparing order: " + number + "");
  cb(number);                                       // Call the callback
}
console.log("Starting to accept order");
for (var i = 0; i < 3; i++) {
  console.log("Taking order: " + i);
  takeOrder(i, order);                              // Pass order as the callback
}
console.log("Job completed!");

That creates a new worker for each order, so they can overlap.

Output:

Starting to accept order
Taking order: 0
Preparing order: 0
Queuing order: 0
Taking order: 1
Preparing order: 1
Queuing order: 1
Taking order: 2
Preparing order: 2
Queuing order: 2
Job completed!
Order: 0 completed
Order: 1 completed
Order: 2 completed

Note how the main thread tells the worker to start via a message, and the worker communicates completion back to the main thread via a message.


Enhancements you could make:

  • order could return a Promise that's fulfilled when the worker finishes its work.
  • You could wait to post the "Job completed" until all the order promises had completed.
  • The worker could post interim updates on its progress back to the main thread.
  • Obviously, you could include more meaningful information in the messages between the worker threads and the main thread.