async / очікувати неявно повертає обіцянку?


111

Я читав, що функції асинхронізації, позначені asyncключовим словом, неявно повертають обіцянку:

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

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

але це не є когерентним ... якщо припустити doSomethingAsync()повернення обіцянки, а ключове слово очікування поверне значення з обіцянки, а не 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показує?
Бармар

це значення, передане функцією вирішення обіцянки, а не сама обіцянка
Олександр Міллз

Можливо, чекайте, коли розгортається результат від обіцянки.
Гамлет Акопян

насправді я помилявся, він записує обіцянку
Олександр Міллз

2
Обіцяння JavaScript намагаються наслідувати асинхронізацію c # в очікуванні поведінки. Однак історично було створено багато структур, які б підтримували це з c #, і жодна в JavaScript. Тож, хоча у багатьох випадках використання це може здатися дуже схожим, це дещо помилково.
Travis J

Відповіді:


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

Обіцяє автоматичне розгортання, тож якщо ви повернете обіцянку за значення в межах 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 функції асинхронізації є - return x; це неявно стає - повернути Promise.resolve (x);
adnan2nd

Чи вважається поганою практикою просто повертати автоматично створену обіцянку, а не явно створювати її самостійно? Якось мені подобається чистий підхід у багатьох випадках.
marlar

24

Я подивився на специфікацію і знайшов наступну інформацію. Коротка версія полягає в тому, що async functiondesugars до генератора, який дає Promises. Отже, так, функції асинхронізації повертають обіцянки .

Відповідно до специфікації tc39 , вірно:

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

Desugars до:

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

1
Очікування дійсне лише в функції асинхронізації
Хан Вам Фем

-3

async не повертає обіцянку, ключове слово очікує на вирішення обіцянки. async - це розширена функція генератора, і очікування працює трохи схоже на вихід

Я думаю, що синтаксис (я не впевнений на 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 є досить типовим генератором на основі 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
" async - це вдосконалена функція генератора " - ні, насправді це не так.
Бергі

Як було сказано вище - "функції асинхронізації" дійсно повертають обіцянку. Принаймні, з концептуальної точки зору, основний пункт заяви "async" полягає в тому, щоб зафіксувати повернені значення цієї функції у обіцянку. Ви навіть можете «чекати» на звичайній старій функції, яка повертає Обіцяння, і все працює, тому що функція 'async' === 'повертає Promise'.
спехтер

2
@bergi, насправді, це розширена функція генератора. функція генератора, яка завжди повертає обіцянку .. чи щось.
Олександр Міллс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.