1

I have the following function that works as expected:

  createObjectFrom(record) {
    let obj = {};

    this.opts.transformers.forEach((transformer, index) => {
      const headerIndex = findIndex(this.headers, (header) => {
        return header === transformer.column;
      });

      const value = transformer.formatter(record[headerIndex]);

      obj[transformer.field] = value;
    });

    return obj;
  }

I want to refactor it to use async await and call an async function in the body of the forEach like this:

  createObjectFrom(record) {
    let obj = {};

    this.opts.transformers.forEach(async (transformer, index) => {
      const headerIndex = findIndex(this.headers, (header) => {
        return header === transformer.column;
      });

      const result = await this.knex('managers').select('name')

      console.log(result);

      const value = transformer.formatter(record[headerIndex]);

      obj[transformer.field] = value;
    });

    return obj;
  }

This will obviously break the function as the forEach is now executing asynchronously and the function will just execute and leave.

Is there a way I can use async await for the forEach to execute in a synchronous manner. Could I refactor to generators?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
dagda1
  • 26,856
  • 59
  • 237
  • 450
  • "This will obviously break the function as the forEach is now executing asynchronously" --- it will not change anything. `Array.prototype.forEach` does not expect to accept `async` functions, so it will invoke it as a "normal" function. So all the functions will be called immediately. – zerkms Sep 03 '16 at 21:57
  • "Is there a way I can use async await for the forEach to execute in a synchronous manner" --- just use a `for` or `for-of` loop to iterate over an array. You will also have to change `createObjectFrom` to be `async`. – zerkms Sep 03 '16 at 21:58
  • You can this kind of stuff smoothly using [this one](https://github.com/toniov/p-iteration), give it a try if you don't mind using a module. – Antonio Val Aug 01 '17 at 06:22

1 Answers1

1

You can not enforce a normal JS function to wait on async behavior. There is no way!

So you would have to refactor your createObjectFrom to be async as well. And then probably go for map/reduce instead of forEach. To be performant you don't want to do this:

for(transformer of this.opts.transformers) {
    await this.knex('managers').select('name');
}

Instead you should use await Promise.all(...).

However in your case the call to knex seems not to depend on the transformer, so you can do this:

async createObjectFrom(record) {
  let obj = {};

  const result = await this.knex('managers').select('name')

  this.opts.transformers.forEach(async (transformer, index) => {
    const headerIndex = findIndex(this.headers, (header) => {
      return header === transformer.column;
    });

    console.log(result);

    const value = transformer.formatter(record[headerIndex]);

    obj[transformer.field] = value;
  });

  return obj;
}

However if you want to do something like fetch for each item something async do it like this:

async foo(data) {
  const subDataArr = await Promise.all(data.map(record => loadSubData(record)));

  return subDataArr;
}
Lux
  • 17,835
  • 5
  • 43
  • 73
  • I'm going to need s knex call for each iteration of the foreach. im curious how this is handled in node as it seems pretty common use case – dagda1 Sep 03 '16 at 22:15
  • @dagda1 thats basically my last code example. Assume `loadSubData` is the Knex call. – Lux Sep 03 '16 at 22:26