ffxsam ffxsam -4 years ago 99
Javascript Question

async/await race condition related to `forEach` use?

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;
},
});

Answer Source

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
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download