1

I have the following async function:

async function createDemoContent({ orgId, userId }) {
  const createTag = promisifyMethod(_createTag);
  const tags = {};

  /* ... */
  [
    'marketing',
    'insights',
    'customer-centric',
    'email marketing',
    'click through rate',
    'tests',
    'research',
    'UX'
  ].forEach(async (label) => {
    const tag = await createTag({
      demo: true,
      label,
      orgId,
    });

    tags[label] = tag._id;
  });

  console.log('all tags:', JSON.stringify(tags));

And just in case anyone wants to see promisifyMethod (it converts a Meteor method to a promisified function call):

export function promisifyMethod(method: Object) {
  return (...args: Array<any>) => {
    return new Promise((resolve: Function, reject: Function) => {
      method.call(...args, (error: Object, result: any) => {
        if (!error) resolve(result);
        else reject(error);
      });
    });
  }
}

For some reason, the tags object is shown as being an empty object. There must be something wrong with my usage of async/await where the console.log is being hit before even the first property is added to the tags object. But I have several await calls above this forEach and those seem to work just fine.

This is the code for createTag:

export const createTag = new ValidatedMethod({
  name: 'createTag',

  validate: new SimpleSchema({
    demo: { type: Boolean, optional: true },
    label: String,
    orgId: String,
  }).validator(),

  run(args) {
    const tag = new Tag(args);

    tag.save();
    return tag;
  },
});
ffxsam
  • 26,428
  • 32
  • 94
  • 144
  • @JaromandaX Added. – ffxsam Mar 24 '17 at 00:08
  • No, `promisifyMethod` is 100% spot on. I've used it dozens of times before. – ffxsam Mar 24 '17 at 00:08
  • That's an interesting problem. I think you are making the assumption that multiple awaits within a for each should behave like `Promise.all`. But it looks like thats not the case. I would push all of the `createTag` promises to an array and then `await Promise.all(promiseArray)` – pizzarob Mar 24 '17 at 00:10
  • 1
    Use Promise.all. Do not use `await` inside loops. – rishat Mar 24 '17 at 00:11

1 Answers1

4

This is because you are calling an async callback function, but is not waiting for it to finish. forEach merely executes the function, but does not wait for the promise returned by the async callback to resolve.

You can do:

for (const label of labels) {
    const tag = await createTag(args)

    tags[label] = tag._id
}
Balázs Édes
  • 13,452
  • 6
  • 54
  • 89
Quan Vuong
  • 1,919
  • 3
  • 14
  • 24