Five Practical Interview Questions Related To Promises

Five Practical Interview Questions Related To Promises

Five interview questions to help you revise Promises quickly.

Assalam u Alaikum & Hello Everyone! ๐Ÿ‘‹

Hope you all are doing well. โœจ

In this article, we are going to learn and revise the knowledge around JavaScript Promises by taking a look at five interview questions related to JavaScript promises API.

Note: These questions were asked to me during my interview rounds at tech companies.

โœ๏ธ Takeaways from this article:

  1. Understand how promises can be executed in parallel.
  2. Racing conditions in promises.
  3. Creating promises and converting callback patterns to promises.
  4. Best practices about when to use or not use Promise.all.
  5. Your Task (For your own exploration ๐Ÿ˜Ž)

Let's start! ๐Ÿš€

Question 01: Sync Vs Parrallel

Please consider the following code and assume it's working 100% correctly without any syntax error. You are presented with two methods to read files from the file system using nodejs.

What's the best way to read files? And Why?

import fs from "fs";
import { promises as fs_ } from "fs";

const data1 = [
  fs.readFileSync("file1.txt"),
  fs.readFileSync("file2.txt"),
  fs.readFileSync("file3.txt"),
];

const data2 = await Promise.all([
  fs_.readFile("file1.txt"),
  fs_.readFile("file2.txt"),
  fs_.readFile("file3.txt"),
]);

Answer:

The first thing to ask the interviewer in such cases (Sync vs Parallel) is whether the promises are dependent on each other or not? If yes, running promises in parallel is not a good option, or sometimes this doesn't solve the problem. In this case, the first approach is perfect.

If the promises are not dependent on each other, then parallel is the robust solution. Why? Because it saves time.

In our case (promises are not dependent on each other), the first approach is perfect but not performant. Therefore, we can opt for the second approach.

However, there is a small drawback of the second approach. That's the short failing nature of Promise.all. If you are using Promise.all and even a single request/promise is rejected, there will be a short circuit and the rest of the requests/promises will be lost. You will also be unable to track the rest of the requests because the code block will fall into the catch case of the try-catch statement.

To avoid this, we can use Promise.allSettled that doesn't perform such short circuits.

You can read my blog Trying Out the Promise.allSettled Combinator for more detailed explanation.

Question 2: Async Await in Race

Please consider the following code and assume it's working 100% correctly without any syntax error. You have two async functions: funcA and funcB. Both have a third-party API call inside them and it's named p and q respectively. After the resolution of the promises, we have two console logs for each function.

Please elaborate on what is going to happen in this case. What will be printed and why?

const funcA = async () => {
  await p;
  console.log('A');
  console.log('B')
}

const funcB = async () => {
  await q;
  console.log('C');
  console.log('D')
}

funcA();
funcB();

Answer:

This is also a traditional problem of racing in promises. Both the funcA and funcB are dependent on other promises and are waiting for them to be resolved. In this scenario, We don't have any idea about which promise is going to be resolved first.

Therefore, the output is dependent on the resolution of promises inside of the async functions.

If we consider that the funcA's promise p is resolved first, then the output will be:

A
B
C
D

Interviewer: Okay, I agree entirely with that. But can we have an output something like A C B D?

Answer: No, because the consoles are completely synchronous operations that don't wait.

Question 3: The Dangerous Race

Note: This question touches on the basics of security and promise racing.

We have tried to simulate an attack on our backend services. We have tried to prevent these sorts of attacks via a timeout pattern. In this implementation, we have a race. We want to return whatever promise is resolved first. Either it will be the dangerousDOSScript or if it's an attack, then our somePromiseWithTimeout is going to be resolved.

const result = await Promise.race(dangerousDOSScript(), somePromiseWithTimeout());

There is a huge problem with this code block. Keeping the same approach of the timeout, can you identify what's wrong?

Hint: There is no syntactical problem. If so, please ignore it.

Answer

The problem here is that either way, the dangerousDOSScript is going to run. And if it contains any malicious code, we are not able to stop it before the timeout time.

One possible solution could be to perform the validations on the backend for the code that's being run.

Question 4: Let's Promisify & Sleep

We have created a small function sleep that takes time in milliseconds and then a second argument a callback that is going to be called after the provided time.

const sleep = (time, callback) => {
  setTimeout(() => {
    callback();
  }, time);
};

Your task is to convert this callback to a promise.

Answer:

1. Conversion of callback pattern to promise:

const sleep = (time) => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, time)
})

Improvements:

We can improve and reduce the syntax by removing the reject function and using one line return syntax of the arrow function. Also, we can directly pass the resolve function as the first parameter of setTimeout because it's going to be called right after the time. Note that, this implementation is pretty simple, the interviewer can ask you about more edge cases and you can always implement them by tweaking the promise code.

const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time))

Question 5: Last One

This question is homework for you. If you are able to guess the output of this question, you have very strong grip of promises and asynchronous world of javascript.

What's the output of the following code snippet?

console.log('start');

const promise1 = Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});

const timer1 = setTimeout(() => {
  console.log('timer1')
  const promise2 = Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)

console.log('end');

Take your time, brainstorm, and dry run it somewhere. If you failed to answer these questions, don't panic. It took me a lot of time to understand this topic and I really don't expect you to be able to crack it in the first go ๐Ÿ˜„

Resources to learn promises:

That's it, folks! hope it was a good read for you. Thank you! โœจ

๐Ÿ‘‰ Follow me: Github Twitter LinkedIn Youtube


ย