异步/等待始终返回Promise

74

我正在尝试异步/等待功能。我有这样的代码来模拟请求:

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

  const json = await request();
  return json;
}
当我以这种方式使用代码时。
console.log(getJSON()); // returns Promise

它返回一个 Promise

但当我调用这行代码时

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

它按预期输出json

是否有可能只使用像console.log(getJSON())这样的代码?我不理解的是什么?


4
"async/await 总是返回 Promise" 这是正确的,因为所有被标记为“async”的函数都将始终返回一个 Promise - 这就是 async/await 的整个意义所在。 - Jaromanda X
console.log(await getJSON()); - hoomi
5个回答

101
每个异步函数都返回一个Promise对象。await语句操作的是一个Promise,等待直到Promise被resolve或reject。
所以,不,即使使用await,您也不能直接对异步函数的结果进行console.log。使用await将使您的函数等待,然后返回一个立即解析的Promise,但它不会为您展开Promise。您仍然需要解开由异步函数返回的Promise,可以使用await或.then()。
当您使用.then()而不是直接使用console.log时,.then()方法使Promise的结果可用于您。但是,您无法从Promise之外获取Promise的结果。这是处理Promises模型的一部分。

21
我认为async/await的全部意义在于使用同步代码编写异步Promise函数。您是在说每次使用async函数后都必须跟随.then()吗?如果是这样,那么这与仅使用Promise有什么区别呢? - Jake Wilson
18
重点是在async函数内部,你可以像编写同步代码一样编写异步代码。但是,在async函数的外部,你需要像平常一样处理该函数的异步结果。通常不会有问题,因为你只应该在用户界面逻辑中获取异步结果。 - Pedro Castilho
14
总之,您需要将异步逻辑编写得像同步逻辑一样,然而,当您从实际上是同步的代码中调用异步逻辑时,您需要区分实际上是同步的和实际上是异步的情况。 - Pedro Castilho
额外的注释是不必要的。缺失的元素是await只是简单地解包承诺,就像.then()做的那样,但返回的是再次包装在一个承诺中。这进一步意味着所有的return new Promise语法糖都为您处理了,所以使函数异步化对您来说是免费的,现在您只需要抛出错误并使用try / catch捕获拒绝或抛出的错误(同一件事)。现在一切都变得非常合理,所以谢谢你! - Kim
1
使用 await 会使您的函数等待,然后返回一个立即解决的 Promise,但它不会为您解开 Promise。如果被 await 的函数可以立即评估,它不会立即解决吗?就像在 OP 的示例中,Promise 需要 2 秒才能解决。 - rb612
如果被等待的函数可以立即评估,那么它不会立即解决吗?就像在OP的例子中,由于setTimeout需要2秒钟,承诺不会立即解决。这就是这个答案令人困惑的地方。显然不是立即的,由于setTimeout需要两秒钟来解决。 - Emre Akı

6

使用 async 关键字定义的函数总是会返回一个 Promise 对象。如果你返回了任何非 Promise 类型的值,它将被隐式地包装在一个 Promise 中。 语句 const json = await request(); 将由 request() 返回的 Promise 解包成一个普通对象 { foo: 'bar' }。 然后,它会被包装在一个 Promise 中,然后从 getJSON 返回,因此当您调用 getJSON() 时,最终得到的是一个 Promise。因此,要解开它,您可以像您所做的那样调用 getJSON().then() 或者执行 await getJSON() 来获取解决的值。


2
在我看来,有太多的包装和解包。如果你把你的答案简短一些,它听起来很有前途。 - Kick Buttowski
我觉得主要的要点是异步函数的返回值总是一个 Promise,因此必须使用 await/then 来获取解析后的值。IKR。 - TheMonarch

5
"async/await" 是一种用于在异步函数中等待其他异步过程完成的方法。无论如何,异步函数的返回值 总是一个 AsyncFunction 对象,当调用时将返回一个 Promise。您无法更改此返回类型。

2
你的第一句话是矛盾的,它不能同时返回Promise和 AsyncFunction 对象。 - loganfsmyth
1
@loganfsmyth,当您定义一个异步函数时,它将返回一个AsyncFunction Object,当调用该函数时,它将返回一个Promise Object。以const t = async () => { ... }为例,t是一个AsyncFunction Object,而t()将返回一个Promise Object - Ozan
3
我的观点是措辞有些令人困惑,它没有返回任何内容,只是计算出了一个值,这个值是一个“AsyncFunction”,而这个函数有一个返回值。async function fn(){}是一个声明,它不返回任何东西,它只是一个AsyncFunction。 - loganfsmyth
我需要在异步方法中显式返回吗?如果我在异步方法中不返回任何内容,会发生什么? - Tamilarasan Rathinam Thangamut

-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();

尝试这个。你可以在主函数中创建一个函数,然后将你的 Promise 代码放在该函数内部。在那里调用它,当你获得响应或错误时,只需返回它。

-1

其他答案已经解释了"每个异步函数都返回一个 Promise 对象",但我猜这种困惑也可能来自于你总是需要在异步函数的实现内部写一次 await,而调用该异步函数的地方还需要再写一次。

这是一个常见的困惑点,例如 为什么我必须两次调用 await 才能获取到 Promise 的解析值?

除了返回值是一个 Promise,MDN 上 await 的控制流效果可以进一步解释:

当代码中遇到 await(无论是在异步函数中还是在模块中),等待的表达式会被执行,同时所有依赖于该表达式值的代码都会被暂停并推入微任务队列。主线程随后会被释放以处理事件循环中的下一个任务。即使等待的值已经是一个已解析的 promise 或者不是一个 promise,这一操作也会发生。

因为“所有依赖于表达式值的代码(在异步函数内部)都会被暂停并推入微任务队列”,调用异步函数的调用者也需要等待(以获取异步函数中的期望结果)。
顺便说一下,这里有一个答案说“如果你想让异步函数立即返回一个值,你可以使用Promise.resolve”是错误的,在返回一个期望的值会返回一个Promise吗?(es7 async/await)中,尽管它得到了一些赞同的票数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接