Javascript 异步/等待隐式返回承诺?

Javascript 异步/等待隐式返回承诺?,javascript,node.js,async-await,ecmascript-next,Javascript,Node.js,Async Await,Ecmascript Next,我了解到,由async关键字标记的异步函数隐式返回一个承诺: async function getVal(){ return await doSomethingAync(); } var ret = getVal(); console.log(ret); 但是这是不一致的…假设doSomethingAsync()返回一个承诺,而await关键字将返回承诺中的值,而不是承诺itsef,那么我的getVal函数应该返回该值,而不是隐式承诺 那么到底是什么情况呢?用async关键字标记的函数是否

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

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

var ret = getVal();
console.log(ret);
但是这是不一致的…假设
doSomethingAsync()
返回一个承诺,而await关键字将返回承诺中的值,而不是承诺itsef,那么我的getVal函数应该返回该值,而不是隐式承诺

那么到底是什么情况呢?用async关键字标记的函数是否隐式返回承诺,或者我们是否控制它们返回的内容

也许,如果我们没有明确地返回某个东西,那么他们就会隐式地返回一个承诺

更清楚地说,以上和以下两者之间存在差异

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
在我的概要中,这种行为确实与传统的返回语句不一致。当从
async
函数显式返回非承诺值时,它将强制将其包装为承诺。
我对它没有太大的问题,但它确实违背了正常的JS。

返回值始终是一个承诺。如果没有显式返回承诺,则返回的值将自动包装在承诺中

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}
返回)

即使有
等待
,也是一样的

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));
承诺自动展开,因此如果您确实从
async
函数中返回值承诺,您将收到值承诺(而不是值承诺)


在我的简介中,这种行为确实与传统的行为不一致 返回语句。当您显式返回 来自异步函数的非承诺值,它将强制在 答应我。我没有什么大问题,但它确实违背了正常规律 JS

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

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

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

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

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

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

异步函数*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.execSql
和at
yield 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);
    });
  });
}

我查看了规范,发现了以下信息。简短的版本是,一个
异步函数
与一个生成
Promise
s的生成器相连接。因此,是的,异步函数返回承诺

根据调查,以下情况属实:

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

调用函数时,只需在函数之前添加wait:

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

您的问题是:如果我创建了一个
异步
函数,它是否应该返回承诺回答:只要做你想做的事情,Javascript就会帮你解决

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

async function getVal(){
    return await doSomethingAsync();
}
完全一样

async function getVal(){
    return doSomethingAsync();
}
你可能在想“WTF,这些怎么可能是一样的?”你是对的。
async
神奇地在必要时用承诺包装一个值


更奇怪的是,可以编写
doSomethingAsync
来有时返回承诺,有时不返回承诺。尽管如此,这两个函数还是完全相同的,因为
等待
也是魔法。如果有必要,它将打开承诺,但对非承诺的内容不会产生任何影响。

console.log
显示了什么?这是承诺解析函数传递的值,而不是承诺本身可能等待打开承诺的结果。JavaScript的承诺试图模仿c#的异步等待行为。然而,历史上有很多结构支持c#,JavaScript中没有。因此,虽然在许多用例中,它可能看起来非常相似,但有点用词不当。是的,这是正确的,只是有点令人困惑,因为它是隐式的…也就是说,即使没有返回语句,它仍然返回承诺…看到了吗?“async是一个增强的生成器函数”-不,事实并非如此。如上所述,“异步函数”确实返回了一个承诺。至少在概念上,“async”语句的要点是将该函数的返回值包装在承诺中。您甚至可以对返回承诺的普通旧函数进行“等待”,这一切都可以正常工作,因为“异步函数”==“函数返回承诺”。@bergi,实际上,它是一个增强的生成器函数。始终返回承诺的生成器函数。。或者,如果异步函数的return语句是-return x,则静态方法promise.resolve会将“返回的值自动包装在promise中”;它隐式地变成了-返回承诺.resolve(x);仅仅返回自动创建的承诺而不是自己显式创建承诺被认为是不好的做法吗?不知何故,我在很多情况下都喜欢干净的方法。“简短的版本是异步函数与生成承诺的生成器相结合。”我认为您可能会将
异步函数
异步函数*
混淆。前者只是回报一个承诺。后者返回一个生成承诺的生成器。这个答案主要是对规范的引用,经过审查,我不认为有任何混淆。确实,异步函数返回承诺,但为了实现这一点,它们对生成承诺的生成器进行了分解。wait仅在异步函数中有效
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); });
    });
}
var ret = await  getVal();
console.log(ret);
async function getVal(){
    return await doSomethingAsync();
}
async function getVal(){
    return doSomethingAsync();
}