Skip to content

Instantly share code, notes, and snippets.

@anvk
Last active October 11, 2023 09:02
Show Gist options
  • Save anvk/5602ec398e4fdc521e2bf9940fd90f84 to your computer and use it in GitHub Desktop.
Save anvk/5602ec398e4fdc521e2bf9940fd90f84 to your computer and use it in GitHub Desktop.
Sequential execution of Promises using reduce()
function asyncFunc(e) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(e), e * 1000);
});
}
const arr = [1, 2, 3];
let final = [];
function workMyCollection(arr) {
return arr.reduce((promise, item) => {
return promise
.then((result) => {
console.log(`item ${item}`);
return asyncFunc(item).then(result => final.push(result));
})
.catch(console.error);
}, Promise.resolve());
}
workMyCollection(arr)
.then(() => console.log(`FINAL RESULT is ${final}`));
@FlyingPumba
Copy link

Thank you !! This example really helped me

@bbalan
Copy link

bbalan commented Mar 22, 2017

Likewise! Many thanks.

@lkaihua
Copy link

lkaihua commented May 8, 2017

I made a few changes to the workMyCollection:

function workMyCollection(arr) {
  return arr.reduce((promise, item) => {
    return promise
      .then((result) => {
        // help you to understand what's result
        console.log(`A: result ${result}, item ${item}`)
        return asyncFunc(item).then(result => {
          final.push(result)
          // print result and item
          console.log(`B: result ${result}, item ${item}`)
          // add a new return value
          return result + " Done"
        })
      })
      .catch(console.error)
  }, Promise.resolve())
}

The new function will help you to understand the sequence better, which outputs following logs:

A: result undefined, item 1
B: result 1, item 1
A: result 1 Done, item 2
B: result 2, item 2
A: result 2 Done, item 3
B: result 3, item 3

@tokland
Copy link

tokland commented Jul 25, 2017

Thanks for sharing. I've taken your code and refactored it with a more functional approach (no let to keep state, we can use the data contained in the promise itself). I've called it promiseMap (maybe promiseSequentialMap would be more precise):

https://gist.github.com/tokland/71c483c89903da417d7062af009da571

@medisoft
Copy link

medisoft commented Sep 6, 2017

Great! Thank you, this helpme a lot with a problem i had!

@izinin
Copy link

izinin commented Oct 4, 2017

This one line Promise.all([1, 2, 3, 4, 5].map(asyncFunc)) does exactly the same . Question: how do you compose different functions in sequential order ?

@rahpuser
Copy link

rahpuser commented Nov 9, 2017

@izinin it doesn't make the same. it looks like doing the same because each time the time increases, make it random, and you would see that the execution is not sequential.

@0xjgv
Copy link

0xjgv commented Nov 16, 2017

@izinin you can use reduce to wait for the first asyncFunc to resolve and then continue:

function getInSequence(array, asyncFunc) {
  return array.reduce((previous, current) => (
    previous.then(accumulator => (
      asyncFunc(current).then(result => accumulator.concat(result))
    ))
  ), Promise.resolve([]));
}

Notice that you pass the accumulator through the Promise.resolve( [] ) and it concats the result of the asyncFunc to it, in the respective order.

Keep in mind that this function will not return the accumulator result right away, it requires an await handler inside an async function to wait for "accumulated promise" to resolve and to handle Promises' errors.

@shankar96
Copy link

var userArray = Object.keys(currentUser);

userArray.reduce((promisechain, value, index) => {
return promisechain.then(() => {
return new Promise((resolve) => {
setTimeout((v,i)=> {
console.log(v,i)
resolve()
},1000,value,index)
})
})
}, Promise.resolve())

@HenriqueKleinberger
Copy link

Thank you, really helped me!

@dnaicker
Copy link

dnaicker commented May 6, 2018

reducing example to nearest simplest form:

[1,2,3].reduce((promise,item) => {
	return promise.then(() => { 
		return new Promise((resolve, reject)=> {
			resolve(item);
		})
	})
},Promise.resolve())

@hipstersmoothie
Copy link

hipstersmoothie commented Jun 1, 2018

await array.reduce(async (promise, item) => {
  await promise;
  return asyncFunction(item);
}, true);

@bradennapier
Copy link

bradennapier commented Aug 16, 2018

I know the goal is to use reduce/be functional here but just to point out using async function and a loop to make sure it works the same way Promise.all does in the end with how it resolves

async function resolveSequentially(arr) {
  const results = [];
  for (const v of arr) {
    if (typeof v === 'function') {
      results.push(await v());
    } else {
      results.push(await v);
    }
  }
  return results;
}

or the reduce parallel

function resolveSequentially(arr) {
  let r = Promise.resolve();
  return Promise.all(
    arr.reduce((p, c) => {
      r = r.then(() => (typeof c === 'function' ? c() : c));
      p.push(r);
      return p;
    }, []),
  );
}

@schmax-max
Copy link

it's friday 4:50pm and this came in at the perfect time. thank you so much!!

@printernet
Copy link

Hey seriously. Echoing the above thanks!

@empz
Copy link

empz commented Dec 1, 2018

Using reduce for this makes it harder than it has to be. It's not very easy on the eyes and mind.
I'd prefer something like this if await / async is available.

// functions is an array of functions that return a promise.
async function runInSequence(functions) {
  const results = [];

  for (const fn of functions) {
    results.push(await fn());
  }

  return results;
}

And we can use it like this:

function promisedFunction(delay, value) {
  return new Promise(resolve => {
    setTimeout(() => resolve(value), delay);
  });
}

console.time("execution");
const results = await runInSequence([
  promisedFunction.bind(this, 1000, 1),
  promisedFunction.bind(this, 1000, 2),
  promisedFunction.bind(this, 1000, 3)
]);

console.timeEnd("execution"); // execution: 3000 ms (approx)
console.log(results); // [1, 2, 3]

@robertoandres24
Copy link

Thanks. Helped me a lot to solution duplicate primary key constraint in save() method with Typeform. The parallel process was my issue and I changed my Promise.all( ) loop, for this fn, and it works great.

@juanccamachob94
Copy link

My pay:

class TasksService {
  static async execute(context, tasks) {
    let results = [];
     await tasks.reduce((promise, task) => {
      return promise
        .then((result) => {
          return TasksService.asyncFunc(task).then(r => {
            results.push(r.bind(context)())
          });
        })
    }, Promise.resolve());
    return results;
  }

  static asyncFunc(f) {
    return new Promise((resolve, reject) => {
      let task = f;
      let time = 0;
      if(typeof(task) !== 'function') {
        task = f.task;
        time = f.time;
      }
      setTimeout(() => resolve(task), time);
    });
  }
}

module.exports = TasksService;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment