Skip to content

Instantly share code, notes, and snippets.

@anvk
Last active September 8, 2024 15:10
Show Gist options
  • Select an option

  • Save anvk/5602ec398e4fdc521e2bf9940fd90f84 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown

Thank you !! This example really helped me

@bbalan

bbalan commented Mar 22, 2017

Copy link
Copy Markdown

Likewise! Many thanks.

@lkaihua

lkaihua commented May 8, 2017

Copy link
Copy Markdown

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

tokland commented Jul 25, 2017

Copy link
Copy Markdown

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

medisoft commented Sep 6, 2017

Copy link
Copy Markdown

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

@izinin

izinin commented Oct 4, 2017

Copy link
Copy Markdown

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

rahpuser commented Nov 9, 2017

Copy link
Copy Markdown

@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

0xjgv commented Nov 16, 2017

Copy link
Copy Markdown

@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
Copy Markdown

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
Copy Markdown

Thank you, really helped me!

@dnaicker

dnaicker commented May 6, 2018

Copy link
Copy Markdown

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

hipstersmoothie commented Jun 1, 2018

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

@bradennapier

bradennapier commented Aug 16, 2018

Copy link
Copy Markdown

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
Copy Markdown

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

@printernet

Copy link
Copy Markdown

Hey seriously. Echoing the above thanks!

@empz

empz commented Dec 1, 2018

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown

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