асинхронность/ожидание неявно возвращает обещание?

#javascript #node.js #async-await #ecmascript-next

Вопрос:

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

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

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

но это несогласованно…предполагая doSomethingAsync() , что возвращает обещание, а ключевое слово await вернет значение из обещания, а не из самого обещания, то моя функция 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. Возможно, ожидание разворачивает результат из обещания.

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

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

Ответ №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 асинхронной функции — return x; он неявно становится — return Promise.resolve(x);

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

3. Нет, я не считаю, что полагаться на автоматически созданное обещание-плохая практика. Я думаю, что предполагаемое последствие асинхронной функции состоит в том, чтобы позволить вам передавать другие функции, возвращающие обещание, без необходимости появления «Обещания» в вашем коде. например, функция async myFunc() { const val1 = ожидание функции otherAsyncFunc1(); const val2 = ожидание функции otherAsyncFunc1(); возврат val1 val 2; } функция async main() { результат const = ожидание функции 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:

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

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

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

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

Ответ №5:

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

Я думаю, что синтаксис (я не уверен на 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. » асинхронность — это улучшенная функция генератора » — нет, это действительно не так.

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

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