异步/等待隐式返回诺言?


111

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

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

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

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

那到底是什么情况?用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语句不一致。似乎当您从async函数显式返回非承诺值时,它将强制将其包装在Promise中。我没有什么大问题,但确实违背了普通的JS。


1
是什么console.log节目?
Barmar

这是承诺解决函数传递的价值,而不是承诺本身
亚历山大·米尔斯

也许等待解开诺言的结果。
哈姆雷特·哈科比亚

实际上,我错了,它兑现了诺言
Alexander Mills

2
JavaScript的承诺正在尝试模仿c#的异步等待行为。但是,从历史上看,有很多结构可以使用c#来支持它,而JavaScript中却没有。因此,尽管在许多用例中,它看起来似乎非常相似,但却有些用词不当。
特拉维斯J

Answers:


138

返回值永远是一个承诺。如果您未明确返回承诺,则返回的值将自动包装在承诺中。

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

即使有,也是一样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自动解包,因此,如果您确实从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中。我没有什么大问题,但是它确实违背了普通的JS。

ES6的函数返回的值与完全不同return。这些功能称为生成器。

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

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

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

3
“返回的值将自动通过Promise.resolve方法包装在Promise中”,即,如果异步函数的return语句为-return x; 它隐式地变成了-return Promise.resolve(x);
adnan2nd

仅返回自动创建的承诺而不是自己显式创建承诺是不明智的做法?在某些情况下,我某种程度上喜欢干净的方法。
马拉

24

我看了一看规格,发现以下信息。简短的版本是async function产生Promises 的生成器的简化。因此,是的,异步函数返回promises

根据tc39规范,以下内容是正确的:

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

“简短的版本是异步函数对生成Promises的生成器减负作用。” 我想你可能会产生混淆async functionasync function*。前者只是返回一个承诺。后者返回产生承诺的生成器。
cdhowie

这个答案很大程度上是对规格的参考,经过审查,我认为不会有任何混淆。的确,异步函数返回promise,但是为了做到这一点,它们将糖分给生成promise的生成器。
乔恩·瑟雷尔


-3

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

4
异步是增强的生成器功能 ”-不,实际上不是。
Bergi '16

如上所述,“异步函数”确实返回了Promise。至少从概念上讲,“ async”语句的要点是将函数的返回值包装在promise中。您甚至可以在返回Promise的普通旧函数上“等待”,并且一切正常,因为'async function'==='function returning Promise'。
壮观的

2
@bergi,实际上,它是增强的生成器功能。始终返回promise ..之类的生成器函数。
亚历山大·米尔斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.