Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it an anti-pattern to use async/await inside of a new Promise() constructor?

People also ask

Can async await be used inside a promise?

Inside an async function you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.

Can I use async in constructor?

A simple answer for that: No, we can't! Currently, class constructors do not return types, and an asynchronous method should return a Task type.

Should I use await in promise all?

Don't use await inside loops. Create an array of Promises and await Promise. all instead.

Can the promise constructor be used to create a promise?

Creating a new PromiseA Promise object is created using the new keyword and its constructor. This constructor takes a function, called the "executor function", as its parameter. This function should take two functions as parameters.


You're effectively using promises inside the promise constructor executor function, so this the Promise constructor anti-pattern.

Your code is a good example of the main risk: not propagating all errors safely. Read why there.

In addition, the use of async/await can make the same traps even more surprising. Compare:

let p = new Promise(resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

with a naive (wrong) async equivalent:

let p = new Promise(async resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

Look in your browser's web console for the last one.

The first one works because any immediate exception in a Promise constructor executor function conveniently rejects the newly constructed promise (but inside any .then you're on your own).

The second one doesn't work because any immediate exception in an async function rejects the implicit promise returned by the async function itself.

Since the return value of a promise constructor executor function is unused, that's bad news!

Your code

There's no reason you can't define myFunction as async:

async function myFunction() {
  let array = await getAsyncArray();
  return new Promise((resolve, reject) => {
    eachLimit(array, 500, (item, callback) => {
      // do other things that use native promises.
    }, error => {
      if (error) return reject(error);
      // resolve here passing the next value.
    });
  });
}

Though why use outdated concurrency control libraries when you have await?


I agree with the answers given above and still, sometimes it's neater to have async inside your promise, especially if you want to chain several operations returning promises and avoid the then().then() hell. I would consider using something like this in that situation:

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)

let p = new Promise((resolve, reject) => {
  (async () => {
    try {
      const op1 = await operation1;
      const op2 = await operation2;

      if (op2 == null) {
         throw new Error('Validation error');
      }

      const res = op1 + op2;
      const result = await publishResult(res);
      resolve(result)
    } catch (err) {
      reject(err)
    }
  })()
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e));
  1. The function passed to Promise constructor is not async, so linters don't show errors.
  2. All of the async functions can be called in sequential order using await.
  3. Custom errors can be added to validate the results of async operations
  4. The error is caught nicely eventually.

A drawback though is that you have to remember putting try/catch and attaching it to reject.


BELIEVING IN ANTI-PATTERNS IS AN ANTI-PATTERN

Throws within an async promise callback can easily be caught.

(async () => {
    try {
        await new Promise (async (FULFILL, BREAK) => {
            try {
                throw null;
            }
            catch (BALL) {
                BREAK (BALL);
            }
        });
    }
    catch (BALL) {
        console.log ("(A) BALL CAUGHT", BALL);
        throw BALL;
    }
}) ().
catch (BALL => {
    console.log ("(B) BALL CAUGHT", BALL);
});

or even more simply,

(async () => {
    await new Promise (async (FULFILL, BREAK) => {
        try {
            throw null;
        }
        catch (BALL) {
            BREAK (BALL);
        }
    });
}) ().
catch (BALL => {
    console.log ("(B) BALL CAUGHT", BALL);
});