'Should I always return a promise when I have an async function?

When I write an async function it usually returns a promise:

export const myPromiseFunction = async (params) => {
  // some logic
  return Promise.resolve('resolved-value');
});

But I was wondering if it would be a mistake if this function would not return a promise, so for example:

export const myPromiseFunction = async (params) => {
  // some logic
  params.map(async (param) => {
    
    await printParam(param);

    async function printParam(par) {
       // do some other stuff
       Printer.print(par);
    });
  });
});

export class Printer {
  public static async print(par) {console.log(par);} // I know it could not be async, but for the sake lets suppose it does
}

Is this a mistake / bad practice ? Or can we find a scenario when this will be valid and desirable ?



Solution 1:[1]

All async functions automatically return Promises. If you declare a function as async, it will return a Promise, even if your only return value is a simple value like a string or number. If you don't return explicitly, your async function will still return a Promise with a value of undefined.

In fact, it is more common for the body of an async function to return a simple value rather than a promise; the assumption is that your function is async because you await the promises you consume within it. As a consequence, even if you return the value 5, the return value is a Promise (that resolves to 5) representing the potential delay that comes from any await expressions in the function.

You don't have to return a Promise object explicitly in your async function, and it is redundant to do so if you're just wrapping a simple value like 'resolved-value'. Conversely, you can make a normal function behave like an async function if you always return a Promise (potentially with Promise.resolve) and you never synchronously throw an error within it.

async function myFunction() {
  makeSomeCall();            // Happens immediately.

  await someOtherPromise();  // myFunction returns a Promise
                             // before someOtherPromise resolves; if it
                             // does without error, the returned Promise
  return 5;                  // resolves to 5.
}

/** Behaves the same as the above function. */
function myFunctionButNotAsync() {
  try {
    makeSomeCall();

    // If you didn't have someOtherPromise() to wait for here, then
    // this is where Promise.resolve(5) would be useful to return.
    return someOtherPromise().then(() => 5);
  } catch (e) {
    return Promise.reject(e);
  }
}

All that said, you may have occasion to explicitly return a Promise object (such as one produced by Promise.all or a separate Promise-returning function), which then observes rules similar to what Promise.resolve() observes: If the object you return from an async function is a Promise, or has a then function, then the automatic Promise the async function returns will wait for the specific Promise or Promise-like object you pass back with return.

async function myFunction() {
  makeSomeCall();            // Happens immediately.
  await anythingElse();      // You can still await other things.

  return someOtherPromise(); // The promise myFunction returns will take
                             // the same outcome as the Promise that
                             // someOtherPromise() returns.
}

In a related sense, this is why return await is seen as redundant, though as described it does make a difference for the stack traces that you see if the wrapped promise is rejected.

Solution 2:[2]

Short answer: no, an async function doesn't have to returns a Promise. Actually, generally you wouldn't return a Promise object (unless you're chaining asynchronous events).

What async and await do is wait for a response from something that returns a Promise.

You first code example actually returns a resolved Promise. But what happens if the Promise isn't resolved properly ?

It's best to call a function that returns a Promise from another async function:

function getRequestResult() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('request sent');
    }, 2000);
  });
}

async function sendMyRequest() {
  console.log('Sending request');
  const result = await getRequestResult();
  console.log(result);
  // expected output: "resolved"
}

You can send rejections/errors within getRequestResult() that way, and also manage how these errors will be managed by the call in sendMyRequest() (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 JeanRemyDuboc