faressoft
faressoft

Reputation: 19651

How to block the event loop until finish running async tasks

Let's assume that we have a web server that receives http requests and send responses back. It has many endpoints including this one

/task should has some async tasks like reading/writing from Redis, I want to block the event loop until those tasks are finished. I know it is not reasonable because the event loop needs also to keep working to receive the events for Redis. So I am thinking about putting this logic in a separate script and use child_process.spawnSync to execute and wait for it and block the current event loop. This solution will work but my problem is my logic and the async tasks are more complex than what mentioned in this sample, they have dependencies to the current script and they are part of the framework, it is not easy to separate them to a separated script. Any suggestions ?

var express = require('express');
var app = express();
var redis = require('./redis');

// Block the event loop until finish doing some async tasks
app.get('/task', function(req, res) {

  // Block the event loop now
  redis.get('backup', function(error, backup) {

    // ...... Do some changes to the backup data

    redis.set('backup', backup, function(error, result) {

      // Unblock the event loop now
      res.send('I am done');

    });

  });

});

// To check if the event loop is really blocked
counter = 0;
setInterval(() => { console.log(counter++) }, 100);

app.listen(5000);

Upvotes: 0

Views: 866

Answers (2)

Edin Jusupovic
Edin Jusupovic

Reputation: 31

You should look into using async/await. You would have to re-write all applicable code to work with Promises or use a library that does so for you.

Your primary function might look something like this;

(async function main(){
 try{

 await taskFunction();
 await backupGetFunction();
 await backupSetFunction();
  res.send('I am done');

 } catch (error){
  throw error;
 }; 

})();

Everything will be executed in order and at the end, it will res.send 'I am done' as expected.

The complexity behind this solution is that any functions that rely on the main function will need to be using promises in order to keep the flow asynchronous.

As an example, your backup function might initially be;

function backupExample(){
 fs.copyFile('source.txt', 'destination.txt', (err) => {
  if (err) throw err;
 console.log('source.txt was copied to destination.txt');
});
}; 

When it must be converted to the following to work with async/await;

function backupExample(){
 return new Promise((resolve, reject) => {
 fs.copyFile('source.txt', 'destination.txt', (err) => {
  if (err) reject(err);
 resolve('source.txt was copied to destination.txt');
  });
 });
}; 

The promises act as guidance to the primary async/await function so it knows when async functions have completed.

Upvotes: 2

John Michael Villegas
John Michael Villegas

Reputation: 336

you can use.

redis.getAsync('backup')
.then(res => {

// ...... Do some changes to the backup data
 return redis.set('backup', backup)
})
.then(()=>res.send('I\'m done'))

or

   let backup = await redis.getAsync('backup');
    // ...... Do some changes to the backup data
   redis.set('backup', backup);
   res.send('I\'m done');

note if you use async/await features you need to have at least node v7 and up. or --harmony-async-await flag when running the node

Upvotes: 0

Related Questions