0

I've read some articles about Promises and async/await in JavaScript to understand its point, but yet have not completely comprehended it; so, I'll explain an example here and want you to help me to comprehend the mechanism. The following code are in NestJS.

Here is my auth.service.ts:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from './user.repository';

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(UserRepository)
    private userRepository: UserRepository
  ) {}

  createUser(): void {
    this.userRepository.createUser();

    console.log('Here I want to do some other works simultaneously while this.userRepository.createUser() is also processing');
  }
}

Here is my user.repository.ts:

import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  async createUser(): Promise<void> {
    await this.makeUser();
  }

  async makeUser(): Promise<void> {
    return new Promise((resolve, reject) => {
      // Simulating making a user which will take some time
      let x = 1;
      for (let i = 0; i < 1000000000; i++) {
        x++;
      }

      console.log('User Created');

      resolve();
    });
  }
}

What I expect based on the above code and what I have learned so far, is that the line console.log('Here I want to do some other works simultaneously...') to work before completion of the line this.userRepository.createUser(), but this does not happen and I get "User Created" logged first and then I get "Here I want to do some other works simultaneously...".

Where am I wrong?

1
  • 8
    Promises do not create threads, JavaScript is still single-threaded. Your for loop is blocking the execution of all other code paths until it completes. Commented Feb 9, 2021 at 19:28

3 Answers 3

2

The problem is that your makeUser() function isn't actually asynchronous. Just because it returns a promise doesn't mean that the work it performs won't block code paths that don't await its completion. JavaScript is still single-threaded, and until the for loop completes, no other code is able to execute.

A more reasonable mock implementation might look like this:

  async makeUser(): Promise<void> {
    return new Promise((resolve, reject) => {
      // Simulating making a user which will take some time
      setTimeout(() => {
        console.log('User Created');

        resolve();
      }, 1000);
    });
  }

Note that setTimeout() is an example of a truly asynchronous function: it schedules a function to be called some time later without blocking the thread. Also note that because JavaScript is single-threaded, the callback function scheduled by setTimeout() will not be executed until all other code paths are finished executing, so this goes both ways, and the following will not cause two simultaneously executing code paths:

setTimeout(() => {
  console.log('scheduled');
}, 50);

let later = Date.now() + 100;

while (later > Date.now()) {}

console.log('done');

In this case, scheduled can never come before done, it will always print afterwards.

Sign up to request clarification or add additional context in comments.

1 Comment

You have to yield to the event loop, in otherwords "stop doing stuff", at least once in a while.
2

Multithreading is possible in JavaScript using Workers.

However since JavaScript is single-threaded, things like Workers are provided by the environment (NodeJS or the browser) you run your JavaScript in. Making http requests or calling setTimeout are good examples of something similar. They are not part of JavaScript itself, but are implemented by the browser or NodeJS instead.

For an example of async work run the snippet below in some JS playground:

(async function () {
 function makeRequest() {
    return fetch('https://jsonplaceholder.typicode.com/todos/1').then(response => response.json()).then(json => console.log("Http request complete", json))
  }
  const promise = makeRequest()
  console.log('Here I want to do work while the http request is made')
  await promise
  console.log("Here I want to continue after http request is complete")
})();

If you want to learn more I recommend this talk and this article

Comments

0

Where am I wrong?

JavaScript is single threaded, so only one piece of code can run at any time.

There are some exceptions like HTTP requests (these are handed off to a thread).

Asynchronous does not mean "parallel".

Synchronous execution is ordered: one thing happens after another:

  1. Do Task 1
  2. Handle result of Task 1
  3. Do Task 2
  4. Handle result of Task 2

Asynchronous execution is not ordered (hence asynchronous):

  1. Do Task 1
  2. Do Task 2
  3. Handle result of Task 1
  4. Handle result of Task 2

You might wonder "Well in your asynchronous example, Do Task 1 and Do Task 2 are executed in parallel". This is not true, asynchronicity is not about parallelism, it's about the "out-of-order" execution.

The power behind asynchronicity is that, if implemented correctly, allows multiple code segments to run "out-of-order" giving the impression, they are run in parallel. *

*This is not to say, that asynchronous code does not allow for parallelism. It's actually the case that asynchronous code allows for parallel processing to happen, since the result is dealt with when it's ready. Of course, if you only ever have one thread, then asynchronicity does not give you any real benefit at all.

7 Comments

"giving the impression, they are run in parallel" I would go one step further and say that the work truly is performed in parallel, the promise just provides a concrete reference through which dependent code paths can await that work's completion. The work is typically done through web APIs (fetch, postMessage, getUserMedia, etc.) or Node.js APIs (fs.readFile, dns.lookup, cp.exec, etc.), which ultimately delegate work to a separate thread or even in-flight network requests, all things which can perform work simultaneously while your JavaScript continues running.
Yes, but that's not a feature of "asynchronicity", it's parallelism.
Asynchronicity has no purpose other than to schedule out-of-order execution based on the completion of work performed elsewhere. The only debatable execption to that might be an RTOS or an OS kernel which schedules tasks based on their estimated execution time given one or more processors that can handle them concurrently.
@PatrickRoberts Yes, I'm coming from a low-level background. Asynchronous does not mean "it happens somewhere else" but "it happens out of order". Like interrupts, they're asynchronous by nature but they have nothing to do with parallelism. Maybe you're right, asynchronicity without handling the work somewhere else doesn't make much sense. Still, just because your function is returning a Promise, or is declared with async does not make it run in a different thread.
I agree with your conclusion, and it's exactly what I said in my comment on the question. But doesn't that go against your claim that "if you only ever have one thread, then asynchronicity does not give you any real benefit at all"? The reason for my initial comment was to point out that "work" isn't restricted to just instructions piping through a CPU, work can be other things that make asynchronicity very useful even with just one thread.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.