async 标签是否用 Promise 包装 JavaScript 函数?

Does async tag wrap a JavaScript function with a Promise?

提问人:webbyweb 提问时间:4/22/2020 更新时间:4/22/2020 访问量:1042

问:

所以我理解“异步”确保函数将返回一个 Promise,如果没有,那么它会将其包装在一个 promise 中。

我的问题是,如果函数已经返回一个 Promise,那么“异步”是否会将其包装在另一个 Promise 中?

async 函数,返回 non-Promise:

async function foo() {
    return 5;
}

console.log(foo()) // Promise { 5 }

regular函数,返回Promise:

function foo() {
    return Promise.resolve(5);
}

console.log(foo()) // Promise { 5 }

async 函数,返回 Promise:

async function foo() {
    return Promise.resolve(5);
}

console.log(foo()) // Promise { <pending> }

为什么最后一个返回 “Promise { pending }” ?我的直觉告诉我,冗余的“异步”标签正在用另一个 Promise 包装已经返回的 Promise。这是正确的吗?

JavaScript 节点.js 承诺 异步等待

评论

0赞 Roamer-1888 4/22/2020
你必须接受这样一个事实,即 Promise 不是为同步检查而设计的,而且同步检查有点奇怪。这种行为可以,也许应该更加一致。

答:

0赞 Mark 4/22/2020 #1

您需要将最后一个函数调用为:

foo.then(function(r){console.log(r)});

原因是,异步函数需要返回一个 promise。在结果得到解决之前,promise 将记录为挂起状态。要获得承诺,您必须调用“then”。

以下是有关以下详细信息的链接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

下面是有关异步 functinos 的详细信息的链接:Here is a link for more info about async functinos: https://fmpapidev.holstein.ca/swagger/index.html

评论

1赞 webbyweb 4/22/2020
这并不能回答我的问题。在第一个示例中,如果我不调用“then”,为什么它会返回“Promise { 5 }”?
0赞 Myk Willis 4/22/2020
@webbyweb 我同意这个答案没有回答你的问题,但是:控制台向你显示“Promise { 5 }”,因为你给 console.log 的对象是一个 Promise,如果你要调用 .then(),它会给你一个值 5。碰巧的是,当你打印它的值时,Promise 已经解析了,但那是因为你用 Promise.resolve() 创建了它。如果您返回了未解析的 Promise,控制台.log将显示“Promise { <pending> }。
2赞 Myk Willis 4/22/2020 #2

如果函数返回 Promise,则该函数返回的 Promise 将解析为与原始 Promise 相同的值。这可以通过一个简单的例子看出:async

async function foo() {
  return Promise.resolve(42);
}
console.log(await foo()); // "42", not "Promise { 42 }"

因此,在大多数正常情况下,我们可以简单地想象,异步函数体中的代码返回的 Promise 是在没有触及的情况下返回的。但是,正如您偶然发现的那样,即使异步函数返回的 Promise 将解析为与代码返回的 Promise 相同的值,但实际的 Promise 对象不一定相同:

let p1;
async function foo() {
  p1 = Promise.resolve(42);
  return p1;
}
let p2 = foo();
console.log('p1 === p2 ?', p1 === p2); // "false" (!)

所以我们可以看到,函数调用返回的 Promise 对象实际上和函数体返回的 Promise 对象是不一样的。但是,无论如何,当我们(或使用)时,它都会给出相同的结果:awaitPromise.then()

let p1;
async function foo() {
    p1 = Promise.resolve(42);
    return p1;
}
let p2 = foo();

console.log(await p1); // '42'
console.log(await p2); // also '42'

(请注意,要在节点 repl shell 中运行这些示例,您需要像这样包装它们:

async function main() {
  /* code using async / await here */
}
main();

评论

0赞 webbyweb 4/22/2020
谢谢@MykWillis这很有帮助。那么,如果一个异步函数已经返回了一个 Promise,那么这个 Promise 就会被解开,然后被包装在“async”标签中的新 Promise 中,这是否准确?
0赞 Myk Willis 4/22/2020
@webbyweb我认为这是基于这些实验的公平描述,尽管老实说,我不确定这是由规范决定的,还是只是特定的实现选择。但可以肯定的是,返回 promise 的异步函数最终不会将嵌套 promise 返回给其调用方(即,解析为另一个 Promise 的 Promise)。
0赞 webbyweb 4/22/2020
知道了,谢谢!顺便说一句,我相信你的回答,你的意思是在第二次定义“foo”时返回 p1。我理解你的意思,但只是为了让其他阅读你的答案的人更清楚地理解这个问题
0赞 Myk Willis 4/22/2020
是的,我的意思是返回 p1,对不起这个遗漏。我已经编辑了答案。
0赞 JohanP 4/22/2020 #3

现在我不确定 / 的原生实现对于现代节点应用程序是什么,但如果你看看它转译到节点 6 时会生成什么asyncawaitBabel

以这个简单的异步函数为例:

async function fooAsync() {
  return 1;
}

Babel 将这段代码转换为如下所示:

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function fooAsync() {
  return _fooAsync.apply(this, arguments);
}

function _fooAsync() {
  _fooAsync = _asyncToGenerator(function* () {
    return 1;
  });
  return _fooAsync.apply(this, arguments);
}

您可以看到异步方法的转换及其生成的状态机。有趣的是,生成器在执行时会返回一个 .因此,要回答您的问题,只需在函数中包含关键字,就会使其返回 .这也可以从编译器发出呻吟的地方看出,如果你有一个方法,并且你没有指定返回类型new Promise()asyncPromiseTypescriptasyncPromise<T>