Search code examples
node.jsmongoose

How to rewrite code (which uses multiple sequential/dependent promises) without nesting await call


I am writing a test case. During cleanup, the test case should

  1. Drop all collections
  2. Confirm collections were dropped
  3. Recreate collections

As far as I know, createCollection and dropCollection return Promises - https://mongoosejs.com/docs/api/connection.html#Connection.prototype.dropCollection()

I want to wait for the Promises to finish in sequence. I thought I'll use await but I can't embed awaits. How can I rewrite the code ?

const collectionsBeforeDrop = await db.collections();
collectionsBeforeDrop.forEach(c => {
      console.log(`got collection name : ${c.collectionName}`);
      await db.dropCollection(c); <-- can't use await here
    });
    console.log(`checking after cleanup`)
    const collectionsAfterDrop = await db.collections();
    collectionsAfterDrop.forEach(c => {
      console.log(`got collection name after claanup! THIS SHOULDN'T HAVE HAPPENED : ${c.collectionName}`);
      throw new Error("couldn't clean up after test case");
    });


console.log(`creating collections again`);
collectionsBeforeDrop.forEach(c => {
  console.log(`got collection name : ${c.collectionName}`);
  await db.createCollection(c) <-- can't use await unless at top level
  .then(()=>{
    console.log(`checking after recreating collections`)
    await db.collections() <-- can't use await unless at top level
    .then((collectionsAfterRecreation)=>{
      collectionsAfterRecreation.forEach(c => {
        console.log(`got collection name after claanup! THIS SHOULDN'T HAVE HAPPENED : ${c.collectionName}`);
      });
    })
  })
});


Solution

  • You can await in the body of a callback. You just have to declare the callback as an async function:

    const collectionsBeforeDrop = await db.collections();
    collectionsBeforeDrop.forEach(async (c) => {
        console.log(`got collection name : ${c.collectionName}`);
        await db.dropCollection(c);
    });
    
    console.log(`checking after cleanup`)
    
    const collectionsAfterDrop = await db.collections();
    collectionsAfterDrop.forEach(c => {
        console.log(`got collection name after claanup! THIS SHOULDN'T HAVE HAPPENED : ${c.collectionName}`);
        throw new Error("couldn't clean up after test case");
    });
    
    console.log(`creating collections again`);
    collectionsBeforeDrop.forEach(async (c) => {
        console.log(`got collection name : ${c.collectionName}`);
        await db.createCollection(c)
            .then(async () => {
                console.log(`checking after recreating collections`)
                await db.collections()
                    .then((collectionsAfterRecreation) => {
                        collectionsAfterRecreation.forEach(c => {
                            console.log(`got collection name after claanup! THIS SHOULDN'T HAVE HAPPENED : ${c.collectionName}`);
                        });
                    })
            })
    });
    

    Untested, adjustments may be needed.