MojoJojo
MojoJojo

Reputation: 4232

Use of Promises for sequential processing

I'm relatively new to Node.js and JavaScript -please excuse if the question below is dumb. To me, promises for async processing make sense but I'm not 100% sure about the use of promises when it comes to serial/sequential processing. Lets look at an example (pseudo code):

Objective: Read file, process what was read from the file and send notification using HTTP post call.

bendUniverseWithoutPromise: function() {

   var data = fs.readFileSync(..); //Read the file
   var result = processData(data);
   this.postNotification(data);

}

In the above function, processData() can not run until we've read the file. And we cannot send the notification until we've finished processing.

Lets look at a slightly different version (assuming each of the above method calls return a promise or we wrap them in a promise):

    bendUniverseWithPromise: function() {
      return new Promise(function() {
         fs.readFileAsync(fileName)
            .then(processData(data))
            .then(postNotification(result))
        })          
    }

Now, my questions are:

  1. Seeing that we require serial/sequential processing in this instance, how is the promise version better than the non promise version? What is it doing better than the first example? Maybe it is a bad example, but then what would be a good example to demonstrate the differences?
  2. Besides the syntax, the promise version adds a little (only a little) in terms readability of code and can get quite complicated with nested promises, context (this!) etc.
  3. I do understand that technically, the first method will NOT return until all processing is done and the second will return immediately and the processing, although still sequential (in context of the method), will carry on in the background.
  4. Is there a general rule regarding the use of promises? Are there any patterns and anti patterns?

Thank you in advance.

Upvotes: 3

Views: 568

Answers (2)

dape
dape

Reputation: 162

I will try to answer all four of your points by taking your example further.

Lets say the first operation (file read) is a slow I/O bound operation and takes 900 ms. The processing and notification are CPU bound and I/O bound respectively, taking 50 ms each. What do the terms “CPU bound” and “I/O bound” mean?

Now, both versions will take the same 1000 ms to complete, but the second example utilizes available resources better, as it is asynchronous. Herein lies the advantage of the promise based version. The first version will make the server completely unresponsive for an entire second, while the second version will only make the server unresponsive during the 50 ms CPU bound processing step.

This hopefully becomes even more lucid when we consider 10 of these requests coming in at the same time. The first example goes through them one at a time, serving request #1 after 1s, #2 after 2s, and so on, finishing after 10s. Its average performance is 1 req/s. The second version would start a file read for request #1, then immediately go on to request #2, spinning up another file read, and so on for all requests. All requests would then finish their reads in around 1s, assuming 100 ms overhead and little or no saturation of disk read bandwidth. The processing would then queue up, taking 500 ms in total for all requests. Lastly we could do the notification posting in parallel, due to it again being I/O-bound. All requests would then in this idealized example be finished in around 1.5 seconds at over 6 req/s, a 6x performance increase. This is exclusively due to the better resourcefulness provided by asynchronicity.

The rule is therefore, always use async/promises when performing I/O bound work.


Sidenote:

Your second example is not correct as there is no data or result variable defined in that scope, the correct version would pass in only the functions to then().

bendUniverseWithPromise: function (fileName) {
  return fs.readFileAsync(fileName)
           .then(processData)
           .then(postNotification)          
}

Upvotes: 2

Amit
Amit

Reputation: 46323

First let's correct the asynchronous code:

bendUniverseWithPromise: function() {
  return fs.readFileAsync(fileName)
           .then(processData)
           .then(postNotification);
}

Now, that above was (almost, had it been complete) an anti-pattern - the explicit construction anti-pattern.

As for why you'd want to use the promised version, well, it's asynchronous .. It allows other operations to take place while asynchronous (mostly I/O) operations are waited for.

Note: fs.readFileAsync does not return a promise by default and needs to be "promisified".

Upvotes: 1

Related Questions