0

I am trying to scale my nodejs app so that it can efficiently handle multiple requests at the same time. One way of achieving this is through code optimization and i wanted to know how I can make time consuming requests async, are promises the only way? or is there another mechanism to achieve the same?

I have already used promises for async tasks but as far as i understand promises, the truly async part is the then and catch handlers. When one creates a promise the executor function will be executed by the main thread until it does some async task (setTimeout, etc).

Sample code of send email

   app.route('/api/contact/sendEmail')
       .post((req, res) =>{
           sendEmail(req).then( () =>{
               res.status(200);
               res.json({
                   sent: true
               });
           }).catch(emailError=> {
               //Custom error send bad request.
               if(emailError.code === 900) {
                   res.status(400);
                   res.json({
                       errorCode: emailError.code,
                       message: emailError.message
                   });
               }else {
                   res.status(500);
                   res.json({
                       errorCode: emailError.code
                   });
               }
           });
       });

The thread is not blocked to send the response but until the sendEmail does not reach the actual async part, the main thread will be blocked

Saurav Seth
  • 301
  • 3
  • 9

1 Answers1

2

Its a bit hard to answer your question since you don't give details about the library/functions you're using, e.g. where the sendMail function is coming from, what app is, and at what point you think something is blocking.

However, in general, your code in Node.JS is always executed in the mainthread. That means, every line your .js file is in the main thread, no matter if anything is "async" or not.

Now, to actually allow something to be "async" (that is: beeing executed while other code is executed), there must be a way to shove stuff into another thread for execution. And this is, what is actually happening in Node.JS under the hood. Node.JS uses libuv / libev to implement an event loop to process tasks. It also features a number of background/worker threads (by default it is 4), which carry out blocking tasks, for example network or file I/O.

However, this is abstracted away from the Node.JS developer, because it is buried in the implementation of the corresponding module. For example, the builtin filesystem or network modules will carry out their tasks in a background thread (unless you use one of the -sync function variants). If you're using a third party module, it depends if it is written in C/C++ and uses libuv to access worker threads and do background processing. If it is not, then your thirdparty module will execute code on the mainthread same as your code, if it is, than execution is really done "async"/in a background thread.

Please also see this very informative thread: How the single threaded non blocking IO model works in Node.js

How does this help you?
Basically I wanted to tell you that it all depends on the functions/modules you are using, and whether or not they are implemented as background operation (using libuv) or not. On top of that, you cannot make anything async/sync yourself, youre bound to use whatever the function/module implements.

For completeness sake I'd like to mention that Node.JS 11 introduced the worker_threads module, which allows to use threads within javascript code, thus allowing you to put something in a background thread and not block the mainthread. Please note that the stability of the module is still "experimental".

Sidenote
Are you really sure that your code is blocking the mainthread? Because as mentioned before, network IO happens in background threads anyway, so it is unlikely that sendMail is blocking the mainthread (assuming that sendMails job is to do network IO). How did you find out it is blocking?

user826955
  • 3,137
  • 2
  • 30
  • 71
  • Ok so if i have to make something non-blocking through my code and if the module or function I use does not make use of background threads then my best bet will be to stick to promises only? app=express() For the side note, i missed adding that sendEmail uses nodemailer. So the actual part of sending the email is, I assume, non-blocking, but till the control reaches to the actual sending of the email my main thread will remain blocked. Thanks for the detailed post about libuv/libve, will check out the article that you have shared as well as the documentation of the experimental module. – Saurav Seth Feb 12 '19 at 08:13
  • A promise just provides a different API to do the same thing. There is no difference in async/non-async. It just allows you to write code in the style of `bla.then().then().then().catch()` and so on, instead of `bla(function callback(...) { moreStuff(function anotherCallback(...) { } }); });`. Same for the `async`/`await` keywords. – user826955 Feb 12 '19 at 09:30
  • Regarding your point "but till the control reaches to the actual sending of the email my main thread will remain blocked", I am not quite sure I understand you. The code you posted attaches a router to your `app`, and attaches a handler to HTTP POST requests to that one. This handler (a function with signature `(req,res) => ()`) is called every time you receive a HTTP request. There is no part which could be referred to with "till the control reaches actual sending", because the first expression in your handler is `sendMail`. – user826955 Feb 12 '19 at 09:33
  • In other words: `app.route().post()` does not block, it sets up your routing, and then returns. The callback function you supply `(req,res) => ()` is called for every request, once it reaches your app. Where do you see actual **blocking**? – user826955 Feb 12 '19 at 09:34
  • As far as i understand, as long as sendEmail doesn't actually start performing IO, the main thread will be blocked. So the post route will not block, but the actual execution of the callback that you mentioned which calls the sendEmail which calls the nodemailer send API, would be blocking till the nodemailer returns a promise (returned once it has started the async email sending). Promises will delay the then and catch handlers by adding them to the end of the queue unlike callbacks which will start executing immediately once called and thus block, right? – Saurav Seth Feb 12 '19 at 10:41
  • In this case this would be an issue of the mail-library, however I find it unlikely that it would spend much time doing "nothing" and then return once IO has started. Is there any bigger content you're trying to send (megabytes, gigabytes) which just take a while to be copied into the worker thread? In any case I don't see how you could solve that on your side. Did you add debug log messages to find out if `sendMail` returns immediately (with a promise) or only after a while? – user826955 Feb 12 '19 at 10:49
  • No It doesn't block how you are thinking. I wanted to add a couple of things which would take some time (it still wouldn't be more than one second), but i was thinking in terms of let's say I get 200 requests in one second then if i add something extra that takes about a second then serving these many requests would be problematic - i understand that for handling so many requests, the server machine must also be scaled. This was more of a theoretical problem that i may or may not face. – Saurav Seth Feb 12 '19 at 10:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/188269/discussion-between-saurav-seth-and-user826955). – Saurav Seth Feb 12 '19 at 11:05