Static methods in the Promise Class

Static methods in the Promise class

Promises are a pattern that helps with one particular kind of asynchronous programming: a function (or method) that returns a single result asynchronously. One popular way of receiving such a result is via a callback (“callbacks as continuations”)


Promise

You can think of a promise as a placeholder, that holds the eventual value for the asynchronous operation.

From MDN

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

If you run this code.

Copy

console.log(new Promise(()=>{}));
  
Output
Copy
➜ Promise {<pending>}
   [[PromiseState]]: "pending"
   [[PromiseResult]]: undefined
  

A promise is an object that contains two properties, A state [[PromiseState]] and a result [[PromiseResult]], we can not interact with these two properties, however these two properties are important when dealing with promises.

The state can be:

  • fulfilled: when a promise resolves successfully.
  • rejected: the promise has been rejected.
  • pending: the promise is still processing.

The result is the value that is passed to either resolve or reject.

for example

Copy

// 1 the value passed when resolved
new Promise((res)=>res(1))

So, in order to initiate a promise we use the new Promise constructor, which takes a function as an argument, that function has two arguments resolve and reject, resolve should be called when the operation is fulfilled, and reject should be called when something went wrong. you can provide one argument to the function and it refers to resolve.

Copy
1const evenPromise = new Promise((resolve,reject)=>{
2    const rand = Math.floor(Math.random() * 10);
3    setTimeout(()=>{
4        // if rand is an even number, resolve; otherwise reject.
5        if(rand % 2 == 0) {
6            resolve("got an even number")
7        }else{
8            reject("got an odd number")
9        }
10    },700)
11  })
12  

Static methods

In this section, I am going to mimic or implement the behavior of these methods.

But before inspecting those methods let's first initialize some promises that we are gonna need to use in this method.

index.js
Copy
1
2// this object will hold the methods.
3const promise = {};
4
5const resolvePromise = Promise.resolve(1);
6
7const resolveWithTimeoutPromise = new Promise((resolve) => {
8  setTimeout(() => {
9    resolve(2);
10  }, 1000);
11});
12
13const thenableObject = {
14  then(res) {
15    setTimeout(() => res("thenable object"), 700);
16  },
17};
18const rejectWithTimeoutPromise = () =>
19  new Promise((resolve, rej) => {
20    setTimeout(() => {
21      rej(2);
22    }, 1000);
23});

1. Promise.resolve(value)

Promise.resolve resolves a value to a promise, if the value is a promise, then promises is returned if the value is a thenable, then it will call then() method with two callbacks it prepared; otherwise, the promise will be fulfilled with value.

index.js
Copy
1
2promise.resolve = (value) => {
3  // if the value is indeed promise then return it as it is.
4  if (typeof value === "object" && typeof value.then === "function") {
5    return value;
6  }
7
8  // otherwise; wrap the value in a promise and return it so user can call then() on it
9  return new Promise((res) => res(value));
10};
11

Let's test it

index.js
Copy
1
2promise.resolve(4).then(console.log);
3promise.resolve(p2).then((res) => console.log(res));
4promise.resolve(p5).then((res) => console.log(res));
5
Output
Copy
➜ node index.js
  4
  thenable object
  2
  

2. Promise.reject(reason)

Promise.reject returns a promise that is rejected for a given reason Promise.reject is used for debugging purposes and selective error catching, it is useful to make reason an instanceof Error.

index.js
Copy
1
2promise.reject = (reason) => {
3  return new Promise((_, rej) => {
4    rej(reason);
5  });
6};
7
8promise.reject("err").catch((err) => console.log(err)); // error
9

3. Promise.all

A function that takes iterable promises and returns a single promise that resolves to an array of the results of the input promises.

this returned promise will be fulfilled when all promises have been fulfilled, or the input iterable doesn't contain promises.

it rejects immediately if one of the iterable promises fails, and it will throw that error. you can think of it like -- all or nothing

index.js
Copy
1
2promise.all = (promises) => {
3  // unify all iterable promises into one promise
4  return new Promise((res, rej) => {
5    // if promises are empty, return an empty array
6    if (promises.length === 0) return res([]);
7
8    // store the results of all promises in one array
9    const resolvedPromises = [];
10
11    // finishedPromises will count how many promises has fulfilled
12    // and if equal to promises.length, I will resolve with resolvedPromises array.
13    let finishedPromises = 0;
14
15    // loop over the promises iterable to call the then methods on it
16    // remember that that user can pass non-promise value,
17    // we have to put that into consideration
18
19    // below, when calling .then() or .catch(), they will get a version of idx,
20    // so they can put the result of that promise into its right place in the array resolvedPromises.
21    promises.forEach((p, idx) => {
22      // here I used Promise. resolve, because the user can pass a non-promise value in the iterable.
23      // Promise.resolve(p): if p is promise it will return it, otherwise; it will returns a promise
24      // that resolves p
25      Promise.resolve(p)
26        .then((value) => {
27          finishedPromises += 1;
28          resolvedPromises[idx] = value;
29
30          // if all promises have been fulfilled, then resolve the unified promise with the results
31          if (finishedPromises === promises.length) res(resolvedPromises);
32        })
33        // if there is a rejection of one of the promises,
34        // then finish and reject the unified promise with the results
35        .catch(rej);
36    });
37  });
38};
39

Let's test it

index.js
Copy
1
2(async () => {
3  // all resolve
4  try {
5    const res = await promise.all([resolvePromise, resolveWithTimeoutPromise, resolvePromise]);
6    console.log(res);
7  } catch (err) {
8    console.log("error :", err);
9  }
10    // one reject
11  try {
12    const res = await promise.all([
13        resolvePromise,
14        resolveWithTimeoutPromise,
15        resolvePromise,
16        rejectWithTimeoutPromise()]);
17    console.log(res);
18  } catch (err) {
19    console.log("error :", err);
20  }
21})();
22
Output
Copy
➜ node index.js
[ 1, 2, 1 ]
error : 2
  

4. Promise.race

Promise.race method takes an iterable of promises and returns first fulfilled or failure, with the value in case of fulfilled or the reason in case of failure.

index.js
Copy
1
2promise.race = (promises) => {
3  return new Promise((res, rej) => {
4    // iterate over the promises iterable
5    for (let p of promises) {
6      // here i used Promise.reolve, because the user can pass a non-promise value in the iterable
7      // Promise.reolve(p): if p is promise it will returns it, otherwise; it will returns a promise
8      // that resolves p
9      Promise.resolve(p)
10        .then((value) => {
11          // once promise is finished with succession, resolve with the value
12          res(value);
13        })
14        .catch((reason) => {
15          // once promise is finished with failur, reject with the reason
16          res(reason);
17        });
18    }
19  });
20};
21

Let's test it

index.js
Copy
1
2(async () => {
3  try {
4    const res = await promise.race([
5        thenableObject,
6        resolvePromise,
7        resolveWithTimeoutPromise,
8        rejectWithTimeoutPromise()
9        ]);   
10    console.log(res);
11  } catch (err) {
12    console.log(err);
13  }
14})();
15
Output
Copy
➜ node index.js
1
  

5. Promise.allSettled

Promise.allsettled method takes an iterable of promises and returns a new promise that resolves after all inputs have settled, either resolved or rejected, it doesn't matter whether it resolves or rejects, it will wait until all promises finish in the end, after calling then() method or using aync/await on Promise.allSettled it will return an array of objects, where every object is represented as {status:"fulfilled", value:result} in case of successful response or {status:"rejected", reason:error} in case of errors
index.js
Copy
1
2promise.allSettled = (promises) => {
3  return new Promise((res, rej) => {
4    // if promises are empty, return an empty array
5    if (promises.length === 0) return res([]);
6
7    // result will hold the objects
8    const results = [];
9
10    // finishedPromises will count how many promises have finished, whether it resolves or rejects
11    // and if equal to promises.length, I will resolve with the results array.
12    let finishedPromises = 0;
13
14    // below, when calling .then() or .catch(), they will get a version of idx,
15    // so they can put the result of that promise into its right place in the array results.
16    promises.forEach((p, idx) => {
17      Promise.resolve(p)
18        .then((value) => {
19          finishedPromises += 1;
20          results[idx] = {
21            status: "fulfilled",
22            value,
23          };
24          if (finishedPromises === promises.length) res(results);
25        })
26        .catch((reason) => {
27          finishedPromises += 1;
28          results[idx] = {
29            status: "rejected",
30            reason,
31          };
32          if (finishedPromises === promises.length) res(results);
33        });
34    });
35  });
36};
37
38

Let's test it

index.js
Copy
1
2(async () => {
3  try {
4    const res = await promise.allSettled([thenableObject,
5    rejectWithTimeoutPromise(),
6    resolvePromise]);
7    console.log(res);
8  } catch (err) {
9    console.log(err);
10  }
11})();
12
13
Output
Copy
➜ node index.js
[
  { status: 'fulfilled', value: 'thenable object' },
  { status: 'rejected', reason: 2 },
  { status: 'fulfilled', value: 1 }
]