5

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 ?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
fewfew
  • 231
  • 2
  • 10
  • 3
    There doesn't appear to be any reason why you would need any of those functions to be async in this example. `printParam` should also not be inside your `map` callback. PS: an async function will _always_ return a promise; not "usually". – Andy Apr 21 '22 at 11:22
  • ^^ To demonstrate the above point: `console.log(async function() {}())`. In your first example, `return Promise.resolve('resolved-value');` and `return 'resolved-value';` will yield the same result. – Felix Kling Apr 21 '22 at 11:23
  • But when I do ```await myPromiseFunction(params)``` in the second example it will return undefined, is this a bad practice in this scenario? – fewfew Apr 21 '22 at 11:29
  • I mean, there is no reason to make `myPromiseFunction` `async` because it doesn't use `await`. But its OK for a promise to resolve to `undefined`. Not all functions return a usable value. – Felix Kling Apr 21 '22 at 13:35

2 Answers2

10

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.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
1

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).