async / await неявно возвращает обещание?

112

Я читал, что асинхронные функции, отмеченные 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.

Александр Миллс
источник
1
Что console.logпоказывает?
Barmar
это значение, переданное функцией разрешения обещания, а не само обещание,
Александр Миллс,
Возможно, await разворачивает результат из обещания.
Гамлет Акопян
на самом деле, я был неправ, это логическое обещание
Александр Миллс
2
Обещания JavaScript пытаются имитировать поведение async await в C #. Однако исторически существовало множество структур для поддержки этого в C #, а в JavaScript - нет. Таким образом, хотя во многих случаях это может показаться очень похожим, это несколько неправильное употребление.
Travis J

Ответы:

139

Возвращаемое значение всегда будет обещанием. Если вы не вернете обещание явно, возвращаемое вами значение будет автоматически заключено в обещание.

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

Обещает автоматическое развертывание, поэтому, если вы вернете обещание для значения из 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));

В моем синопсисе поведение действительно несовместимо с традиционными операторами возврата. Похоже, что когда вы явно возвращаете значение, не являющееся обещанием, из асинхронной функции, оно принудительно помещает его в обещание. У меня нет большой проблемы с этим, но он бросает вызов обычному JS.

ES6 имеет функции, которые не возвращают то же значение, что и return. Эти функции называются генераторами.

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

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

// Logs 'test'.
console.log(foo().next().value);
Натан Уолл
источник
3
«возвращаемое вами значение будет автоматически заключено в обещание» статическим методом Promise.resolve, то есть, если оператор возврата асинхронной функции - return x; неявно становится - return Promise.resolve (x);
adnan2nd
Считается плохой практикой просто возвращать автоматически созданное обещание вместо того, чтобы явно создавать его самостоятельно? Почему-то мне во многих случаях нравится чистый подход.
Марлар
24

Я взглянул на спецификацию и нашел следующую информацию. Краткая версия состоит в том, что async functionдесахарирует генератор, который дает 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); });
    });
}
Джон Суррелл
источник
«Краткая версия заключается в том, что асинхронная функция обессахаривает генератор, который выдает обещания». Я думаю , вы можете быть запутанным async functionс async function*. Первый просто возвращает обещание. Последний возвращает генератор, который дает обещания.
cdhowie
Этот ответ в значительной степени является ссылкой на спецификацию, и после обзора я не думаю, что есть какая-то путаница. Это правда, асинхронные функции возвращают обещания, но для этого они обессахаривают генераторы, которые выдают обещания.
Джон Суррелл,
-1

Просто добавьте ожидание перед своей функцией, когда вы ее вызываете:

var ret = await  getVal();
console.log(ret);
Mohsen Gharivand
источник
1
await действует только в асинхронной функции
Хан Ван Фам,
-3

async не возвращает обещание, ключевое слово await ожидает разрешения обещания. 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.execSql и в yield connection.callProcedure

Функция db.exec - довольно типичный генератор на основе обещаний.

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);
    });
  });
}
akc42
источник
4
« async - это улучшенная функция генератора » - нет, на самом деле это не так.
Берги
Как указано выше - «асинхронные функции» действительно возвращают обещание. По крайней мере, концептуально основной смысл инструкции async - заключить возвращаемые значения функции в обещание. Вы даже можете «ждать» в простой старой функции, которая возвращает обещание, и все это работает, потому что «асинхронная функция» === «функция, возвращающая обещание».
Spechter
2
@bergi, на самом деле, это улучшенная функция генератора. функция-генератор, которая всегда возвращает обещание .. или что-то в этом роде.
Александр Миллс