ConductedClever
ConductedClever

Reputation: 4295

Using `Async` keyword while returning a promise object

I am developing some Nodejs scripts and I have a question about the usage of async keyword in JS itself.

For example, consider that I have a function which returns a promise like this:

function myFunction () {
  ... some job
  return new Promise(async (resolve, reject) => {
    const innerResult = await someHeavyJob();
    ... some job
    resolve(innerResultThatIsManipulated);
  });
}

And I can write it in another way of this:

async function myFunction () {
  ... some job
  const innerResult = await someHeavyJob();
  ... some job
  return innerResultThatIsManipulated;
}

But I have a question that if I am choosing the first solution, then am I allowed to use async keyword and is that recommended or not? For example, something like this:

async function myFunction () {
  ... some job
  return new Promise(async (resolve, reject) => {
    const innerResult = await someHeavyJob();
    ... some job
    resolve(innerResultThatIsManipulated);
  });
}

To emphasize that this is an async method and is returning promise (not a simple object) in all paths of its code.

So, what is the recommended way to use?

Upvotes: 0

Views: 230

Answers (3)

trincot
trincot

Reputation: 350137

This is an anti pattern:

return new Promise(async (resolve, reject) => {
  const innerResult = await someHeavyJob();
  // ... some job
  resolve(innerResultThatIsManipulated);
});

You should not create a new promise with new Promise when the constructor callback is going to create a promise itself and then call resolve with that (manipulated) promised result.

This really should be coded like this:

return someHeavyJob().then((innerResult) =>
  // ... some job
  return innerResultThatIsManipulated;
});

The difference

So after the above remark, your question really is about adding the async keyword to this function definition:

function myFunction () {
  // ... some job 1
  return someHeavyJob().then((innerResult) =>
    // ... some job 2
    return innerResultThatIsManipulated;
  });
}

Here are the main differences:

  • If an exception is raised before .then() is called -- so in "some job 1" or while calling someHeavyJob -- then the async version will still return a promise (which has a rejected state), while the non-async version will raise that error.

  • Also, the promise returned by the async version is not the one returned in the return statement. The one that async returns is a separate promise, one whose resolution is (planned to be) bound to the one you provide to the return statement.

You could take this difference away, by writing the non-async version like this:

function myFunction () {
  return Promise.resolve().then(() => {
    // ... some job 1
    return someHeavyJob().then((innerResult) =>
      // ... some job 2
      return innerResultThatIsManipulated;
    });
  });
}

Or, better, flattened-out to one chain:

function myFunction () {
  return Promise.resolve().then(() => {
    // ... some job 1
    return someHeavyJob();
  }).then((innerResult) =>
    // ... some job 2
    return innerResultThatIsManipulated;
  });
}

This better mimics what the async version does: that one ensures to return a promise.

So, in conclusion, just adding async is not producing a function that is guaranteed to be behave in the same way. If your aim is to always return a promise, even when an error occurs while the function executes, then definitely go for async. But once you do, there are often good reasons to also use await syntax: it just makes the code more readable.

Upvotes: 2

Davi
Davi

Reputation: 4147

Of all examples you've shown, only the second one is correct. The reason behind that is that Promise is a constructor function (hence you use it with new) that takes a regular function, not another Promise as it's argument.

Example 1

The interpreter translates your first example into something like this (theoretically):

new Promise( new Promise((resolve, reject) => { ... }))

That's not what you want and also considered an anti-pattern. Instead, you want this:

new Promise((resolve, reject) => { ... })

Furthermore, it seems you already have a function (someHeavyJob) that results in a Promise. Why do you want to wrap it up in another Promise? Just use the .then method of the Promise returned by someHeavyJob to transform whatever it gives you.

In the end, you are left with something like:

function myFunction() {
  // ... some job
  return someHeavyJob().
    then( result => {
      // ... some job with result
    })
}

Example 3

The third example is incorrect for the same reasons example 1 has been incorrect, speaking from a technical as well as from a logical point of view.

General rules of thumb

Don't use async and await (only) as a means to communicate that certain functions are asynchronous in nature to other people (or your future self). Both keywords have implicit effects on the code you write and make it behave different.

Try to stick to either of both conventions (using Promise or using async/await). Only mix them if you really need to.

Personally, I try to stick to Promise instead of async/await, because they often screw people up and – to me – they both don't add much of a benefit besides making code "look nicer". However, that's just my personal opinion.

Upvotes: 1

user12603016
user12603016

Reputation:

MDN says:

The async function declaration defines an asynchronous function — a function that returns an AsyncFunction object. Asynchronous functions operate in a separate order than the rest of the code via the event loop, returning an implicit Promise as its result. But the syntax and structure of code using async functions looks like standard synchronous functions.

By doing this:

async function myFunction () {
  return new Promise(async (resolve, reject) => {
    const innerResult = await someHeavyJob();
    resolve(innerResult);
  });
}

You're returning an implicit promise that returns a promise. You asked if it would be an interesting approach to get the code more readable, I believe it confuses more than it clarifies.

(not even talking about the async function inside the promise inside the async function that actually contains only that promise that contains only that async function, the first one I talked about not the last one, I mean the second one, did you follow? Edit: you've actually edited that snippet, it was just for the example)

MDN adds:

The purpose of async/await is to simplify using promises synchronously

In my comprehension, an async function is an alternative a promise, not an extension per say. Therefore, it seems not wise to combine the two of them the way you proposed.

Upvotes: 1

Related Questions