73

I'm trying async/await functionality. I have such code imitating a request:

const getJSON = async () => {
  const request = () => new Promise((resolve, reject) => (
    setTimeout(() => resolve({ foo: 'bar'}), 2000)
  ));

  const json = await request();
  return json;
}

When I use the code in this way

console.log(getJSON()); // returns Promise

it returns a Promise

but when I call this line of code

getJSON().then(json => console.log(json)); // prints { foo: 'bar' }

it prints json as expected

Is it possible to use just code like console.log(getJSON())? What don't I understand?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
user3309314
  • 2,453
  • 2
  • 17
  • 30

5 Answers5

99

Every async function returns a Promise object. The await statement operates on a Promise, waiting until the Promise resolves or rejects.

So no, you can't do console.log on the result of an async function directly, even if you use await. Using await will make your function wait and then return a Promise which resolves immediately, but it won't unwrap the Promise for you. You still need to unwrap the Promise returned by the async function, either using await or using .then().

When you use .then() instead of console.logging directly, the .then() method makes the result of the Promise available to you. But you can't get the result of the Promise from outside the Promise. That's part of the model of working with Promises.

Pedro Castilho
  • 10,174
  • 2
  • 28
  • 39
  • 21
    I thought the whole point of async/await was so that you could write async Promise functions using synchronous code. Are you saying that you always have to follow up an `async` function with `.then()`? If so, then how is that different than just using a Promise by itself? – Jake Wilson May 16 '17 at 21:53
  • 18
    The point is that _within_ the `async` function, you can write async code as if it were synchronous. However, from _outside_ the `async` function, you need to treat the asynchronous result of that function as usual. Which is usually not a problem, since you should be only getting asynchronous results in your UI logic anyway. – Pedro Castilho May 17 '17 at 19:28
  • 14
    In summary: You write your async logic as if it were synchronous, however when you call your async logic from code that is _actually_ synchronous, you need to differentiate between actually-synchronous and actually-asynchronous. – Pedro Castilho May 17 '17 at 19:29
  • The extra comments were not needed. The missing element was that await simply unpacks the promise in the same way that .then() does, but what is returned is again wrapped in a promise. And that further means that all the return new Promise syntactic sugar is handled for you, so making a function async does that for you for free and you need only throw errors now and use try / catch to catch rejects or thrown errors (same thing). It all makes so much sense now, so thanks a million! – Kim Mar 31 '18 at 20:09
  • 1
    "Using `await` will make your function wait and then return a Promise which resolves immediately, but it won't unwrap the Promise for you." Won't it only resolve immediately if the function being `await`ed could be immediately evaluated? Like in OP's example, the promise doesn't resolve immediately as it takes 2 seconds. – rb612 Dec 30 '19 at 08:45
  • "Won't it only resolve immediately if the function being awaited could be immediately evaluated? Like in OP's example, the promise doesn't resolve immediately as it takes 2 seconds." That's what's so confusing about this answer. It's obviously not immediate, it'll take two seconds to resolve due to `setTimeout`. – Emre Akı Jun 22 '23 at 17:18
6

A function defined with async always returns a Promise. If you return any other value that is not a Promise, it will be implicitly wrapped in a Promise. The statement const json = await request(); unwraps the Promise returned by request() to a plain object { foo: 'bar' }. This is then wrapped in a Promise before being returned from getJSON so a Promise is what you ultimately get when you call getJSON(). So to unwrap it, you can either call getJSON().then() like you've done or do await getJSON() to get the resolved value.

TheMonarch
  • 397
  • 4
  • 11
  • 2
    so many warpping and unwrapping IMHO, if you make your answer shorter, it sounds promising – Kick Buttowski Apr 07 '20 at 03:00
  • IKR, I guess the main takeaway is that the return value of an async function is always a promise and must therefore be used with await/then to get the resolved value. – TheMonarch Apr 08 '20 at 20:51
5

Return value of an async function will always be an AsyncFunction Object, which will return a Promise when called. You can not change that return type. The point of async/await is to easily wait for other async process to complete inside an async function.

Ozan
  • 3,709
  • 2
  • 18
  • 23
  • 2
    Your first sentence is contradictory, it can't return both a Promise and an `AsyncFunction` object. – loganfsmyth Apr 15 '17 at 06:36
  • 1
    @loganfsmyth, when you define an async function, it will return a `AsyncFunction Object`, which will return a `Promise Object` **when called**. Take `const t = async () => { ... }` for example, `t` is an `AsyncFunction Object` and `t()` will return a `Promise Object` – Ozan Apr 15 '17 at 06:58
  • 3
    Right my point is the wording is confusing, there is no return, it just evaluates to a value that is an `AsyncFunction` and that function has a return. `async function fn(){}` is a declaration, it doesn't return anything, it just _is_ an AsyncFunction. – loganfsmyth Apr 15 '17 at 07:30
  • Do I need to explictly return for async method?how it will if i dont return anything in async method – Tamilarasan Rathinam Thangamut Jan 03 '20 at 07:22
-1
const getResOrErr = () => {
  const callAsyncCodeHere = async () => {
  const request = () =>
      new Promise((resolve, reject) =>
        setTimeout(() => resolve({ foo: "bar" }), 2000)
      );

    const json = await request();
    return json;
  };
  return callAsyncCodeHere()
   .then(console.log)
   .catch(console.log);
};

getResOrErr();

Try this. You can achieve making a function inside your main function and then put you promise code inside that function. Call it there and when you get the response or error just return it.

-1

Other answers already explain "Every async function returns a Promise object" but I guess the confusion may also come from the fact that you always need to write await twice, one inside the async function implementation (getJSON() in your case) and one for the caller of that async function.

This is a common point of confusion, e.g. Why do I have to call await twice in order to get the promise resolved?

Except for the return value is a promise, Control flow effects of await in MDN can further explain,

When an await is encountered in code (either in an async function or in a module), the awaited expression is executed, while all code that depends on the expression's value is paused and pushed into the microtask queue. The main thread is then freed for the next task in the event loop. This happens even if the awaited value is an already-resolved promise or not a promise.

Because "all code that depends on the expression's value (inside the async function) is paused and pushed into the microtask queue", the caller of the async function also needs to await (to get the awaited result in the async function).

BTW, one answer here said "if you want your async function to return a value immediately you can use Promise.resolve" is wrong in Returning an awaited value returns a Promise? (es7 async/await) even though it got some upvotes.

Qiulang
  • 10,295
  • 11
  • 80
  • 129