async/await 隐式返回 promise?

async/await implicitly returns promise?

提问人:Alexander Mills 提问时间:2/10/2016 最后编辑:Felix KlingAlexander Mills 更新时间:10/5/2022 访问量:134895

问:

我读到由关键字标记的异步函数隐式返回一个承诺:async

async function getVal(){
 return await doSomethingAync();
}

var ret = getVal();
console.log(ret);

但这并不连贯......假设返回一个 promise,并且 await 关键字将返回 promise 的值,而不是 promise itsef,那么我的 getVal 函数应该返回该值,而不是隐式 promise。doSomethingAsync()

那么到底是怎么回事呢?由 async 关键字标记的函数是隐式返回 promise,还是我们控制它们返回的内容?

也许如果我们没有显式返回某些东西,那么它们就会隐式返回一个承诺......?

更清楚地说,上述和

function doSomethingAync(charlie) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(charlie || 'yikes');
        }, 100);
    })
}

async function getVal(){
   var val = await doSomethingAync();  // val is not a promise
   console.log(val); // logs 'yikes' or whatever
   return val;  // but this returns a promise
}

var ret = getVal();
console.log(ret);  //logs a promise

在我的概要中,该行为确实与传统的 return 语句不一致。当您从函数显式返回非 promise 值时,它会强制将其包装在 promise 中。 我对它没有大问题,但它确实违背了普通的 JS。async

JavaScript 节点.js async-await ecmascript-next

评论

1赞 Barmar 2/10/2016
显示什么?console.log
0赞 Alexander Mills 2/10/2016
它是 promise 解析函数传递的值,而不是 promise 本身
1赞 Hamlet Hakobyan 2/10/2016
也许 await 从承诺中解开结果。
3赞 Travis J 2/10/2016
JavaScript 的 promise 试图模仿 c# 的异步 await 行为。但是,历史上有很多结构来支持 c#,而 JavaScript 中没有。因此,虽然在许多用例中它看起来非常相似,但它有点用词不当。
1赞 Alexander Mills 8/5/2018
是的,这是正确的,只是有点令人困惑,因为它是隐含的......又名,即使没有 return 语句,它仍然返回一个 promise......明显?

答:

230赞 Nathan Wall 2/10/2016 #1

返回值将始终是 promise。如果未显式返回 promise,则返回的值将自动包装在 promise 中。

async function increment(num) {
  return num + 1;
}

// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));

即使没有回报也是一样的!(返回)Promise { undefined }

async function increment(num) {}

即使有 .await

function defer(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(callback());
    }, 1000);
  });
}

async function incrementTwice(num) {
  const numPlus1 = await defer(() => num + 1);
  return numPlus1 + 1;
}

// Logs: 5
incrementTwice(3).then(num => console.log(num));

Promise 自动解包,因此,如果您确实从函数中返回了值的 promise,您将收到该值的 promise(而不是该值的 promise 的 promise)。async

function defer(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(callback());
    }, 1000);
  });
}

async function increment(num) {
  // It doesn't matter whether you put an `await` here.
  return defer(() => num + 1);
}

// Logs: 4
increment(3).then(num => console.log(num));

在我的概要中,这种行为确实与传统不一致 return 语句。似乎当您显式返回一个 非 promise 值,它会强制将其包装在一个 承诺。我对它没有大问题,但它确实违背了正常 WSS的

ES6 的函数返回的值与 .这些函数称为生成器。return

function* foo() {
  return 'test';
}

// Logs an object.
console.log(foo());

// Logs 'test'.
console.log(foo().next().value);

评论

10赞 adnan2nd 11/13/2017
静态方法 Promise.resolve 将“你返回的值将自动包装在一个 promise 中”,即如果异步函数的 return 语句为 - return x;它隐式变为 - return Promise.resolve(x);
0赞 marlar 5/16/2020
只返回自动创建的承诺而不是自己显式创建它被认为是不好的做法吗?不知何故,在许多情况下,我喜欢干净的方法。
1赞 RonJRH 7/21/2021
不,我不认为依赖自动创建的承诺是不好的做法。我认为异步函数的预期结果是允许您通过管道连接到其他返回 promise 的函数,而不必在代码中出现“Promise”。例如,函数 async myFunc() { const val1 = await otherAsyncFunc1(); const val2 = await otherAsyncFunc1(); return val1 + val 2; } function async main() { const result = await myFunc(): console.log(“结果是” + result“); }
-10赞 akc42 2/10/2016 #2

async 不返回 promise,await 关键字等待 promise 的解析。async 是一个增强的生成器函数,await 的工作方式有点像 yield

我认为语法(我不是 100% 确定)是

async function* getVal() {...}

ES2016 生成器函数的工作方式有点像这样。我做了一个基于繁琐的数据库处理程序,你像这样编程

db.exec(function*(connection) {
  if (params.passwd1 === '') {
    let sql = 'UPDATE People SET UserName = @username WHERE ClinicianID = @clinicianid';
    let request = connection.request(sql);
    request.addParameter('username',db.TYPES.VarChar,params.username);
    request.addParameter('clinicianid',db.TYPES.Int,uid);
    yield connection.execSql();
  } else {
    if (!/^\S{4,}$/.test(params.passwd1)) {
      response.end(JSON.stringify(
        {status: false, passwd1: false,passwd2: true}
      ));
      return;
    }
    let request = connection.request('SetPassword');
    request.addParameter('userID',db.TYPES.Int,uid);
    request.addParameter('username',db.TYPES.NVarChar,params.username);
    request.addParameter('password',db.TYPES.VarChar,params.passwd1);
    yield connection.callProcedure();
  }
  response.end(JSON.stringify({status: true}));

}).catch(err => {
  logger('database',err.message);
  response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});

请注意我是如何像正常同步一样对其进行编程的,尤其是在

yield connection.execSqlyield connection.callProcedure

db.exec 函数是一个相当典型的基于 Promise 的生成器

exec(generator) {
  var self = this;
  var it;
  return new Promise((accept,reject) => {
    var myConnection;
    var onResult = lastPromiseResult => {
      var obj = it.next(lastPromiseResult);
      if (!obj.done) {
        obj.value.then(onResult,reject);
      } else {
       if (myConnection) {
          myConnection.release();
        }
        accept(obj.value);
      }
    };
    self._connection().then(connection => {
      myConnection = connection;
      it = generator(connection); //This passes it into the generator
      onResult();  //starts the generator
    }).catch(error => {
      reject(error);
    });
  });
}

评论

6赞 Bergi 2/10/2016
"async 是一个增强的生成器函数“——不,它真的不是。
0赞 spechter 5/8/2018
如上所述 - “异步函数”确实返回了 Promise。至少从概念上讲,“async”语句的要点是将该函数的返回值包装在一个 promise 中。你甚至可以“等待”一个返回 Promise 的普通旧函数,它都可以工作,因为 'async function' === 'function returning Promise'。
3赞 Alexander Mills 8/18/2018
@bergi,实际上,它是一种增强的生成器功能。一个生成器函数,它总是返回一个 promise 。什么的。
37赞 Jon Surrell 2/10/2016 #3

是的,异步函数将始终返回 promise。

根据 tc39 规范,脱糖到产生 s 的发生器。async functionPromise

具体说来:

async function <name>?<argumentlist><body>

脱糖至:

function <name>?<argumentlist>{ return spawn(function*() <body>, this); }

其中“是对以下算法的调用”:spawn

function spawn(genF, self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self);
        function step(nextF) {
            var next;
            try {
                next = nextF();
            } catch(e) {
                // finished with failure, reject the promise
                reject(e);
                return;
            }
            if(next.done) {
                // finished with success, resolve the promise
                resolve(next.value);
                return;
            }
            // not finished, chain off the yielded promise and `step` again
            Promise.resolve(next.value).then(function(v) {
                step(function() { return gen.next(v); });
            }, function(e) {
                step(function() { return gen.throw(e); });
            });
        }
        step(function() { return gen.next(undefined); });
    });
}

评论

1赞 cdhowie 1/9/2020
“简而言之,异步函数脱糖到产生 Promise 的生成器。”我想你可能会混淆.前者只是返回一个承诺。后者返回一个产生 promise 的生成器。async functionasync function*
5赞 Jon Surrell 1/9/2020
这个答案很大程度上是对规范的参考,经过审查,我认为没有任何混淆。没错,异步函数返回 promise,但为了做到这一点,它们会去糖化到产生 promise 的生成器。
-3赞 mohsen gharivand 10/28/2019 #4

只需在调用函数时在函数前添加 await:

var ret = await  getVal();
console.log(ret);

评论

8赞 Han Van Pham 4/16/2020
await 仅在异步函数中有效
26赞 John Henckel 11/4/2020 #5

你的问题是:如果我创建一个函数,它是否应该返回一个承诺?:只要做你想做的任何事情,Javascript 就会为你修复它。async

假设是一个返回 promise 的函数。然后doSomethingAsync

async function getVal(){
    return await doSomethingAsync();
}

完全相同

async function getVal(){
    return doSomethingAsync();
}

你可能在想“WTF,这些怎么可能是一样的?”你是对的。如有必要,将神奇地用 Promise 包装值。async

更奇怪的是,可以写成有时返回一个承诺,有时返回一个承诺。尽管如此,这两个功能是完全相同的,因为这也是神奇的。如有必要,它将解开 Promise,但对非 Promise 的内容没有影响。doSomethingAsyncawait