1. Promises

JavaScript is a synchronous programming language. However, callback functions enable us to transform it into an asynchronous programming language. Promises are to help to get out of “callback hell” while dealing with the asynchronous code and do much more.

Promise is always Async

const promise = new Promise((resolve, reject) => {
  let sheLovesMe = true;
  if (sheLovesMe) {
    resolve("Let's Marry");
  } else {
    reject("Let's Breakup");
  }
});

promise
  .then((val) => { return val })
  .then((val) => console.log(val))
  .catch((val) => console.log(val))
  .finally(() => console.log("All sorted now"));

// Output:
Let's Marry
All sorted now

2. Async Await

New syntax introduced in ES6 that helps to process the promise in a better way.

const promise = Promise.resolve("I am happy");

async function example(promise) {
  try {
    const response = await promise;
    console.log("Try: " + response);
  } catch (error) {
    console.log("Catch: " + error);
  } finally {
    console.log("All set");
  }
}

example(promise);

// Output
Try: I am happy
All set

----------

// const promise = Promise.reject("I am sad");
Catch: I am sad
All set

3. Synchronous v/s Asynchronous

Sync means line by line execution. Sync is a single-thread, so only one operation will run at a time.

Async means code execution in a non-blocking manner. Code which takes time will be executed later. Async is multi-thread, which means operations can run in parallel.

console.log("start")
console.log("middle")
console.log("end")

// Synchronous
start
middle
end

console.log("start")
setTimeout(() => console.log("middle"), 1000)
console.log("end")

// Asynchronous
start
end
middle (after 1s)

4. Callback

Due to Async nature we will see weird code outputs. So inorder to prevent that we use callbacks.

Passing function as a argument to another function is Callback.

function middleAction(name) {
    setTimeout(() => {
        return "Hello " + name
    }, 1000)
}

console.log("start")
let message = middleAction("Arjunan", )
console.log(message)
console.log("end")

// start
// undefined
// end

-----

function middleAction(name, cb) {
  setTimeout(() => {
      cb("Hello " + name)
  }, 1000)
}

console.log("start")
middleAction("Arjunan", (message) => {
  console.log(message)
})
console.log("end")

// start
// end
// Hello Arjunan

5. Pyramid of Doom / Callback Hell

To fix Async code issues using callbacks in large code base result in a pyramid of callbacks. This is known as Pyramid of Doom / Callback Hell

function middleAction(name, cb) {
  setTimeout(() => {
    cb("Hello " + name);
  }, 1000);
}

function whatIsTheAge(age, cb) {
  setTimeout(() => {
    cb("Age is " + age);
  }, 500);
}

function whatIsThePlace(place, cb) {
  setTimeout(() => {
    cb("Place is " + place);
  }, 100);
}

console.log("start");
middleAction("Arjunan", (message) => {
  console.log(message);
});
whatIsTheAge(12, (message) => {
  console.log(message);
});
whatIsThePlace("London", (message) => {
  console.log(message);
});
console.log("end");

// start
// end
// Place is London
// Age is 12
// Hello Arjunan
middleAction("Arjunan", (message) => {
  console.log(message);
  whatIsTheAge(12, (message) => {
    console.log(message);
    whatIsThePlace("London", (message) => {
      console.log(message);
      whatIsTheJob("Dev", (message) => {
        console.log(message);
        whatIsTheDomain("Frontend", (message) => {
          console.log(message);
        });
      });
    });
  });
});

// Hello Arjunan
// Age is 12
// Place is London
// Job is Dev
// Domain is Frontend

6. Callback Hell to Promises

Nice we have Promise Hell now :)

function middleAction(name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + name);
    }, 2000);
  });
}

function whatIsTheAge(age) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + age);
    }, 500);
  });
}

function whatIsThePlace(place) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + place);
    }, 1000);
  });
}

console.log("start");
middleAction("Arjunan").then((res) => {
  console.log(res);
  whatIsTheAge(12).then((res) => {
    console.log(res);
    whatIsThePlace("London").then((res) => console.log(res));
  });
});
console.log("end");
Promise.resolve(console.log("Promise Resolved"));

// start
// end
// Promise Resolved
// Hello Arjunan
// Hello 12
// Hello London

7. Use Promise Chaining

console.log("start");
middleAction("Arjunan")
  .then((res) => {
    console.log(res);
    return whatIsTheAge(12);
  })
  .then((res) => {
    console.log(res);
    return whatIsThePlace("London");
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });
console.log("end");
Promise.resolve(console.log("Promise Resolved"));

// start
// end
// Promise Resolved
// Hello Arjunan
// Hello 12
// Hello London

8. Promise.all

Takes a array of promises. It rejects if any one promise reject. It resolves only if all promises are resolved.

Promise.all keeps the order of array irrespective of execution time.

function whatIsTheName(name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + name);
    }, 2000);
  });
}

function whatIsTheAge(age) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + age);
    }, 500);
  });
}

function whatIsThePlace(place) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + place);
    }, 1000);
  });
}

Promise.all([
  whatIsTheName("Arjunan"),
  whatIsTheAge(12),
  whatIsThePlace("London"),
])
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: " + err));

// [ 'Hello Arjunan', 'Hello 12', 'Hello London' ]

-----

function whatIsTheName(name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Hello " + name);
    }, 2000);
  });
}

// ERROR!
// Error: Hello Arjunan

9. Promise.race

It returns the first promise which gets resolved or rejected. Time is the preference, not fullfillment.

10. Promise.allSettled

It returns all promises in a array even it is resolved or rejected.

11. Promise.any

Similar to race but ignores rejected promise and returns the first resolved promise.

If all promises are rejected it returns, Error: AggregateError: All promises were rejected.

12. What is the Output?

JS engine find the Sync code inside the Promise so it will executed in order and at last it will execute Async code.

console.log("start");
let promise1 = new Promise((resolve, reject) => {
  console.log(1);
  resolve(2);
  console.log(3);
});
promise1.then((res) => {
  console.log(res);
});
console.log("end");

start
1
3
end
2

13. What is the Output?

console.log("start");
let promise1 = new Promise((resolve, reject) => {
  console.log(1);
  console.log(3);
});
promise1.then((res) => {
  console.log("Result: " + res);
});
console.log("end");

start
1
3
end

14. What is the Output?

console.log("start");

const fn = () => {
  new Promise((resolve, reject) => {
    console.log(1);
    resolve("Success");
  });
};

fn().then((res) => {
  console.log(res);
});
console.log("end");

// TypeError: Cannot read properties of undefined (reading 'then')

console.log("start");

const fn = () => {
  return new Promise((resolve, reject) => {
    console.log(1);
    resolve("Success");
  });
};

fn().then((res) => {
  console.log(res);
});
console.log("end");

// start
// 1
// end
// Success

15. What is the Output?

function job() {
  return new Promise((resolve, reject) => {
    reject();
  });
}

let promise = job();

promise
  .then(() => {
    console.log("Success 1");
  })
  .then(() => {
    console.log("Success 2");
  })
  .then(() => {
    console.log("Success 3");
  })
  .catch(() => {
    console.log("Error 1");
  })
  .then(() => {
    console.log("Success 4");
  });

// Error 1
// Success 4

16. What is the Output?

function job(state) {
  return new Promise((resolve, reject) => {
    if (state) {
      resolve("Success");
    } else {
      reject("Failed");
    }
  });
}

let promise = job(true);

promise
  .then((data) => {
    console.log(data);
    return job(false);
  })
  .catch((err) => {
    console.log(err);
    return "Error Caught";
  })
  .then((data) => {
    console.log(data);
    return job(true);
  })
  .catch((err) => {
    console.log(err);
    return "Error Caught";
  });

// Success
// Failed
// Error Caught

17. Throw v/s New Error

New Error will not go inside catch. it is basically just a return of data, which is a not a actual error.

throw "something" or throw new Error will definetly go inside catch. It is actually throwing a error.

function job(state) {
  return new Promise((resolve, reject) => {
    if (state) {
      resolve("Success");
    } else {
      reject("Failed");
    }
  });
}

let promise = job(true);

promise
  .then((data) => {
    console.log(data);
    return job(true);
  })
  .then((data) => {
    if (data !== "Victory") {
      throw "Defeat";
    }
    return job(true);
  })
  .then((data) => {
    console.log(data);
  })
  .catch((err) => {
    console.log(err);
    return job(false);
  })
  .then((data) => {
    console.log(data);
    return job(true);
  })
  .catch((err) => {
    console.log(err);
    return "Error Caught";
  })
  .then((data) => {
    console.log(data);
    return new Error("test");
  })
  .then((data) => {
    console.log("Success 1: " + data.message);
    throw new Error("test");
  })
  .then((data) => {
    console.log("Success 2: " + data.message);
  })
  .catch((data) => {
    console.log("Error: " + data.message);
  });

// Success
// Defeat
// Failed
// Error Caught
// Success 1: test
// Error: test

18. Promise Chaining I

const firstPromise = new Promise((resolve, reject) => {
  resolve("first");
});

const secondPromise = new Promise((resolve, reject) => {
  resolve("second");
});

secondPromise.then((res) => {
  firstPromise.then((res) => {
    console.log(res);
  });
  console.log(res);
});

// second
// first

19. Promise Chaining II

const firstPromise = new Promise((resolve, reject) => {
  resolve("first");
});

const secondPromise = new Promise((resolve, reject) => {
  resolve(firstPromise);
});

secondPromise
  .then((res) => {
    return res;
  })
  .then((res) => {
    console.log(res);
  });

// first

20. Rewrite in Async/Await instead of then/catch

loadJSON("https://fakeurl.com/no-such-user-exist.json")
  .then((res) => console.log(res))
  .catch((err) => console.log(err));

// Then & Catch
function loadJSON(url) {
  return fetch(url).then((response) => {
    if (response.status == 200) {
      return response.json();
    } else {
      throw new Error("No JSON exist: " + response.status);
    }
  });
}

// Async & Await
async function loadJSON(url) {
  const response = await fetch(url);
  if (response.status == 200) {
    const data = await response.json()
    return data
  }
  throw new Error("No JSON exist: " + response.status);
}

21. Recursive Promises

function whatIsTheName(name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + name);
    }, 2000);
  });
}

function whatIsTheAge(age) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + age);
    }, 500);
  });
}

function whatIsThePlace(place) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Hello " + place);
    }, 1000);
  });
}

function PromiseRecursive(promises) {
  if (promises.length === 0) return;

  let currPromise = promises.shift();
  currPromise.then((res) => console.log(res)).catch((err) => console.log(err));
  PromiseRecursive(promises);
}

PromiseRecursive([
  whatIsTheName("Arjunan"),
  whatIsTheAge(12),
  whatIsThePlace("London"),
]);

// Hello 12
// Hello London
// Hello Arjunan

22. Promises & setTimeout output

setTimeout(() => {
    console.log('start')
}, 0);

Promise.resolve('start promise').then((res) => console.log(res))

new Promise((res, rej) => {
    console.log('1')
    setTimeout(() => { console.log('2') }, 0);
    res('response')
    return;
    console.log('3');
}).then((res) => {
    setTimeout(() => {
        console.log('4')
    }, 0);
    console.log(res);
})

// ouput
// 1
// start promise
// response
// Promise {<fulfilled>: undefined}
// start
// 2
// 4