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

#javascript

#javascript #node.js #async-await #ecmascript-следующий

Вопрос:

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

2. это значение, переданное функцией разрешения обещания, а не само обещание

3. Возможно, await разворачивает результат из promise.

4. Обещания JavaScript пытаются имитировать поведение async await в c #. Однако исторически существовало много структур для поддержки этого с помощью c #, и ни одной в JavaScript. Таким образом, хотя во многих случаях использования это может показаться очень похожим, это несколько неправильное название.

5. да, это правильно, просто немного сбивает с толку, поскольку оно неявное … иначе, даже если нет оператора return, он все равно возвращает обещание … видел?

Ответ №1:

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

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

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

Комментарии:

1. «возвращаемое вами значение будет автоматически обернуто в обещание» статическим методом Promise.resolve , то есть, если оператор возврата асинхронной функции — return x ; он неявно становится — return Promise.resolve(x);

2. Считается ли плохой практикой просто возвращать автоматически созданное обещание вместо того, чтобы явно создавать его самостоятельно? Почему-то мне нравится чистый подход во многих случаях.

3. Нет, я не считаю, что полагаться на автоматически созданное обещание — плохая практика. Я думаю, что предполагаемым следствием асинхронной функции является то, что она позволяет вам переходить к другим функциям, возвращающим обещание, без необходимости появления «Promise» в вашем коде. например, функция async myFunc() { const val1 = await otherAsyncFunc1(); const val2 = await otherAsyncFunc1(); return val1 val2; } функция async main() { const result = await myFunc(): console.log(«Результат равен » результат»); }

Ответ №2:

Я взглянул на спецификацию и нашел следующую информацию. Короткая версия заключается в том, что async function функция десугаризуется генератору, который выдает Promise s . Итак, да, асинхронные функции возвращают обещания.

Согласно спецификации 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); });
    });
}
 

Комментарии:

1. «Короткая версия заключается в том, что асинхронная функция отключается от генератора, который выдает обещания». Я думаю, вы можете сбить async function с async function* толку. Первый просто возвращает обещание. Последний возвращает генератор, который выдает обещания.

2. Этот ответ в значительной степени является ссылкой на спецификацию, и после проверки я не верю, что есть какая-либо путаница. Это правда, асинхронные функции возвращают обещания, но для этого они отключаются от генераторов, которые дают обещания.

Ответ №3:

Ваш вопрос: если я создаю async функцию, должна ли она возвращать обещание или нет? Ответ: просто делайте все, что хотите, и Javascript исправит это за вас.

Предположим doSomethingAsync , что это функция, которая возвращает обещание. Затем

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

точно так же, как

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

Вы, вероятно, думаете: «Черт возьми, как они могут быть одинаковыми?» и вы правы. async При необходимости волшебным образом обернет значение обещанием.

Еще более странно, doSomethingAsync что может быть написано, чтобы иногда возвращать обещание, а иногда и НЕ возвращать обещание. Тем не менее, обе функции точно такие же, потому await что это тоже волшебство. При необходимости он развернет обещание, но это не повлияет на вещи, которые не являются обещаниями.

Ответ №4:

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

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

Комментарии:

1. ожидание допустимо только в асинхронной функции

Ответ №5:

async не возвращает обещание, ключевое слово await ожидает разрешения обещания. async — это улучшенная функция генератора, и await работает немного как yield

Я думаю, что синтаксис (я не уверен на 100%)

async function* getVal() {...}

Функции генератора ES2016 работают примерно так. Я создал обработчик базы данных, основанный на top of tedious, который вы программируете следующим образом

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

Комментарии:

1. » async — это улучшенная функция генератора » — нет, на самом деле это не так.

2. Как указано выше, «асинхронные функции» действительно возвращают обещание. По крайней мере, концептуально, основной смысл инструкции ‘async’ заключается в том, чтобы обернуть возвращаемые значения этой функции в обещание. Вы можете даже ‘await’ для простой старой функции, которая возвращает обещание, и все это работает, потому что ‘async function’ === ‘функция, возвращающая обещание’.

3. @bergi, на самом деле, это улучшенная функция генератора. функция генератора, которая всегда возвращает обещание .. или что-то в этом роде.