最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Sequential execution of Promise.all - Stack Overflow

programmeradmin2浏览0评论

Hi I need to execute promises one after the other how do I achieve this using promise.all any help would be awesome. Below is the sample of my code I am currently using but it executes parallel so the search will not work properly

public testData: any = (req, res) => {
    // This method is called first via API and then promise is triggerd 
    var body = req.body;

    // set up data eg 2 is repeated twice so insert 2, 5 only once into DB
    // Assuming we cant control the data and also maybe 3 maybe inside the DB
    let arrayOfData = [1,2,3,2,4,5,5];

    const promises = arrayOfData.map(this.searchAndInsert.bind(this));

    Promise.all(promises)
        .then((results) => {
            // we only get here if ALL promises fulfill
            console.log('Success', results);
            res.status(200).json({ "status": 1, "message": "Success data" });
        })
        .catch((err) => {
            // Will catch failure of first failed promise
            console.log('Failed:', err);
            res.status(200).json({ "status": 0, "message": "Failed data" });
        });
}

public searchAndInsert: any = (data) => {
    // There are database operations happening here like searching for other
    // entries in the JSON and inserting to DB
    console.log('Searching and updating', data);
    return new Promise((resolve, reject) => {
        // This is not an other function its just written her to make code readable
        if(dataExistsInDB(data) == true){
            resolve(data);
        } else {
            // This is not an other function its just written her to make code readable
            insertIntoDB(data).then() => resolve(data);
        }
    });
}

I looked up in google and saw the reduce will help I would appreciate any help on how to convert this to reduce or any method you suggest (Concurrency in .map did not work)

Hi I need to execute promises one after the other how do I achieve this using promise.all any help would be awesome. Below is the sample of my code I am currently using but it executes parallel so the search will not work properly

public testData: any = (req, res) => {
    // This method is called first via API and then promise is triggerd 
    var body = req.body;

    // set up data eg 2 is repeated twice so insert 2, 5 only once into DB
    // Assuming we cant control the data and also maybe 3 maybe inside the DB
    let arrayOfData = [1,2,3,2,4,5,5];

    const promises = arrayOfData.map(this.searchAndInsert.bind(this));

    Promise.all(promises)
        .then((results) => {
            // we only get here if ALL promises fulfill
            console.log('Success', results);
            res.status(200).json({ "status": 1, "message": "Success data" });
        })
        .catch((err) => {
            // Will catch failure of first failed promise
            console.log('Failed:', err);
            res.status(200).json({ "status": 0, "message": "Failed data" });
        });
}

public searchAndInsert: any = (data) => {
    // There are database operations happening here like searching for other
    // entries in the JSON and inserting to DB
    console.log('Searching and updating', data);
    return new Promise((resolve, reject) => {
        // This is not an other function its just written her to make code readable
        if(dataExistsInDB(data) == true){
            resolve(data);
        } else {
            // This is not an other function its just written her to make code readable
            insertIntoDB(data).then() => resolve(data);
        }
    });
}

I looked up in google and saw the reduce will help I would appreciate any help on how to convert this to reduce or any method you suggest (Concurrency in .map did not work)

Share Improve this question edited Feb 25, 2019 at 14:04 Akshay Venugopal asked Feb 25, 2019 at 13:28 Akshay VenugopalAkshay Venugopal 4713 gold badges9 silver badges22 bronze badges 11
  • Have you tried async await that will resolve your problem. – Azeem Aslam Commented Feb 25, 2019 at 13:29
  • How do I do that can you share anything – Akshay Venugopal Commented Feb 25, 2019 at 13:30
  • 1 Take a look of this link mean while I will look for problem in your existing code. javascript.info/async-await – Azeem Aslam Commented Feb 25, 2019 at 13:32
  • 1 Avoid the Promise constructor antipattern in searchAndInsert! – Bergi Commented Feb 25, 2019 at 13:34
  • 2 Possible duplicate of Resolve promises one after another (i.e. in sequence)? – sigmus Commented Feb 25, 2019 at 13:55
 |  Show 6 more comments

4 Answers 4

Reset to default 12

the Promises unfortunatelly does not allow any control of their flow. It means -> once you create new Promise, it will be doing its asynchronous parts as it likes.

The Promise.all does not change it, its only purpose is that it checks all promises that you put into it and it is resolved once all of them are finished (or one of them fail).

To be able to create and control asynchronous flow, the easiest way is to wrap the creation of Promise into function and create some kind of factory method. Then instead of creating all promises upfront, you just create only one promise when you need it, wait until it is resolved and after it continue in same behaviour.

async function doAllSequentually(fnPromiseArr) {
  for (let i=0; i < fnPromiseArr.length; i++) {
    const val = await fnPromiseArr[i]();
    console.log(val);
  }
}

function createFnPromise(val) {
  return () => new Promise(resolve => setTimeout(() => resolve(val), 200));
}

const arr = [];
for (let j=0; j < 10; j++) {
  arr.push(createFnPromise(j));
}

doAllSequentually(arr).then(() => console.log('finished'));

PS: It is also possible without async/await using standard promise-chains, but it requires to be implemented with recursion.

If anyone else cares about ESLint complaining about the use of "for" and the "no await in loop" here is a typescript ESLint friendly version of the above answer:

async function runPromisesSequentially<T>(promises: Array<Promise<T>>):Promise<Array<T>> {
  if (promises.length === 0) return [];
  const [firstElement, ...rest] = promises;
  return [await firstElement, ...(await runPromisesSequentially(rest))];
}

You can then just replace Promise.all by runPromisesSequentially.

@lmX2015's answer is close but it's taking in promises that have already started executing.

A slight modification fixes it

export async function runPromisesSequentially<T>(functions: (() => Promise<T>)[]): Promise<T[]> {
  if (functions.length === 0) {
    return [];
  }
  const [first, ...rest] = functions;

  return [await first(), ...(await runPromisesSequentially(rest))];
}

The original post asked how the poster might convert their code into a .reduce().

Here is one possible way that uses a .reduce(), processes each promise one at a time before processing the next, and adheres to most modern linting rules:

try {
  const results = await promises.reduce(async (acc, curr) => {
    return [
      ...(await acc),
      await this.searchAndInsert.bind(this)(curr),
    ];
  }, []);
  // we only get here if ALL promises fulfill
  console.log('Success', results);
  res.status(200).json({ "status": 1, "message": "Success data" });
} catch (err) {
  // Will catch failure of first failed promise
  console.log('Failed:', err);
  res.status(200).json({ "status": 0, "message": "Failed data" });
}

Of course, this doesn't read extremely well, and if you have to do this one time in your code base, it is often likely you'll want to do it more than once. It's best to wrap up the reduce into a reusable function.

const mapAsyncSequential = async (listItems, mapFn) => {
    return listItems.reduce(
        async (acc, curr, i) => {
            return [
                ...acc,
                await mapFn(curr, i, list),
            ];
        },
        [],
    );
}

and then you can rewrite the first code snippet with the .reduce() line updated to use the new convenience function:

try {
  const results = await mapAsyncSeqential(promises, this.searchAndInsert.bind(this));
  // we only get here if ALL promises fulfill
  console.log('Success', results);
  res.status(200).json({ "status": 1, "message": "Success data" });
} catch (err) {
  // Will catch failure of first failed promise
  console.log('Failed:', err);
  res.status(200).json({ "status": 0, "message": "Failed data" });
}

Considering that the original Promise.all() strategy would typically work much faster, just some points to keep in mind:

  • Processing sequentially should be limited to situations where you absolutely need it. For example, if each promise depends on the previous one finishing, or if processing all promises at the same time causes server issues.
    • If your issue is with server problems due to too many requests in sequence, another slightly more complex approach is to "chunk" your array up and process each chunk sequentially, but their contents with Promise.all().
  • Using a .reduce() has a memory cost. For small arrays, this is usually negligible, but if you're processing a large enough data set, you may want to write mutative code (for...of, etc) to ensure performance is not compromised.
发布评论

评论列表(0)

  1. 暂无评论