Implement mapSeries async function in JavaScript | Frontend Interview question

Implement mapSeries async function in JavaScript | Frontend Interview question

Hey everyone , I was solving some problems recently and found a very interesting question on Learner's bucket website, btw if you don't know Learner's bucket is very good platform for practicing Javascript problem solving, haha not sponsered :D

So, the question is -
Create a JavaScript function named mapSeries that functions similarly to the standard Array.map() method but is designed for asynchronous operations. Unlike Array.map(), which processes elements in parallel, mapSeries should handle each input sequentially, processing one input at a time.

The mapSeries function should return a promise. This promise should resolve with an array containing the results of applying an asynchronous iteratee function to each input in the order they were received. If any iteratee invocation results in an error, the promise should reject immediately with this error.

  1. Iteratee Function:

    • The iteratee is an asynchronous function that takes two arguments:

      • Input: The current item to be processed.

      • Callback: A callback function that the iteratee must call once processing is complete.

    • The callback function should have two parameters:

      • Error: Should be truthy if an error occurs during the processing of the input, otherwise null.

      • Result: The result of processing the input. This should only be considered if the error is null.

  2. Behavior:

    • Inputs should be processed in series; the next input should only be processed once the current one has been fully handled.

    • If the iteratee calls the callback with an error, mapSeries should immediately reject the promise with this error and cease processing further inputs.

    • If all inputs are processed without errors, mapSeries should resolve with an array of results corresponding to the inputs.

I hope you got the idea what question is asking to do, I am going to solve it using recursion , though it's can be solved in many ways and on learner's bucket website it's solved using array.reduce method but after going through that solution i felt it was bit confusing, that's why I am using recursion.

So here we go

// First of all create a function with any name you want
// this function should take two params , array and iteratee fn
function mapSeries (input, iteratee){
   // do as the question says, it should return a promise.
  return new Promise((resolve, reject) => {
      // a variable to store all the values, 
      const final = [];
       // a variable to track where we are in the loop of input
      let index = 0;
       // this function is to call the iteratee fn depending 
       // on the condition 

      function processNext(){
        // if index less than input length that means
        // all inputs are not yet passed to iteratee ,.. easy ?
        if(index < input.length){
            iteratee(input[index], ((err, result) => {
              // as the question suggest that iteratee fn takes input
              // and a callback with params err and result
               // if its err check and reject
              if(err){
                reject(err)
              }else {
                 // storing the value returned by iteratee fn
                final.push(result)
                index++
                // calling the function itself, it will fail after
                // after all the input is processed
                processNext()
              }
            }))
        }else {
          // resolving if every input is processed
          resolve(final)
        }
      }
      // invoking processNext fn
      processNext()
  })
}

Test

let numPromise = mapSeries([1, 2, 3, 4, 5], function (num, callback) {
  setTimeout(function () {
    num = num * 2;
    console.log(num);

    // throw error
    if(num === 12){
      callback(true);
    }else{
      callback(null, num);
    }

  }, 2000);
});

numPromise
  .then((result) => console.log("success:" + result))
  .catch(() => console.log("no success"));

Output:
// each number will be printed after a delay of 2 seconds
2
4
6
8
10
"success:2,4,6,8,10" // this will be printed immediately after last