Search code examples
node.jstypescriptes6-promise

JS: Why Promise then() method executes synchronously?


I need to make part of my method's code asynchronous, so it will execute in a non-blocking manner. For this purpose I've tried to create a "dummy" Promise and put the specified code in then block. I have something like this:

public static foo(arg1, arg2) {
const prom = new Promise((resolve, reject) => {
   if (notValid(arg1, arg2)) {
      reject();
   }
   resolve(true);
});

prom.then(() => {
    ...my code using arg1 and arg2...
});
}

However, then block always executes synchronously and blocks whole app, even though each and every JS documentation tells that then always runs asynchronously. I've also tried to replace the Promise with this:

Promise.resolve().then(() => {
    ...my code using arg1 and arg2...
});

but got same result. The only way I've managed to make then block work asynchronously is by using setTimeout:

const pro = new Promise(resolve => {
            setTimeout(resolve, 1);
        });

pro.then(() => {
   ...my code using arg1 and arg2...
})

What can be the reason behind then block working synchronously? I don't want to proceed with using setTimeout, because it is kind of a "dirty" solution, and I think that there should be a way to make then run asynchronously.

Application is NodeJS with Express, written using Typescript.


Solution

  • I need to make part of my method's code asynchronous, so it will execute in a non-blocking manner. For this purpose I've tried to create a "dummy" Promise and put the specified code in then block.

    Promises don't really make things asynchronous in and of themselves. What they do is wrap around something that's already asynchronous, and give a convenient way to tell when that thing is done. If you wrap a promise around something synchronous, your code is mostly still synchronous, just with a few details about when the .then callback executes.

    even though each and every JS documentation tells that then always runs asynchronously.

    By that, they mean that it waits for the current call stack to finish, and then runs the code in the .then callback. The technical term for what it's doing is a "microtask". This delay is done so that the order of operations of your code is the same whether the promise is already in a resolved state, or if some time needs to pass before it resolves.

    But if your promise is already resolved (eg, because it's wrapped around synchronous code), the only thing your .then callback will be waiting for is the currently executing call stack. Once the current call stack synchronously finishes, your microtask runs synchronously to completion. The event loop will not be able to progress until you're done.

    The only way I've managed to make then block work asynchronously is by using setTimeout

    setTimeout will make things asynchronous*, yes. The code will be delayed until the timer goes off. If you want to wrap this in a promise you can, but the promise is not the part that makes it asynchronous, the setTimeout is.

    I don't want to proceed with using setTimeout, because it is kind of a "dirty" solution

    Ok, but it's the right tool for the job.


    * when i say setTimeout makes it asynchronous, i just mean it delays the execution. This is good enough for many cases, but when your code eventually executes, it will tie up the thread until it's done. So if it takes a really long time you may need to write the code so it just does a portion of the work, and then sets another timeout to resume later