техническая разница между асинхронной функцией ES7 и обещанием?

Я пытаюсь лучше понять, что такое async function в JavaScript с технической точки зрения, даже если я в основном знаю, как их использовать.

Многие знакомства с async/await заставляют поверить, что функция async в основном является просто обещанием, но это явно не так (по крайней мере, не с код, транспилированный Babel6):

async function asyncFunc() {
  // nop
}

var fooPromise = new Promise(r => setTimeout(r, 1));

console.clear();

console.log("typeof asyncFunc is", typeof asyncFunc); // function
console.log("typeof asyncFunc.next is", typeof asyncFunc.next); // undefined
console.log("typeof asyncFunc.then is", typeof asyncFunc.then); // undefined

console.log("typeof fooPromise is", typeof fooPromise); // object
console.log("typeof fooPromise.next is", typeof fooPromise.next); // undefined
console.log("typeof fooPromise.then is", typeof fooPromise.then); // function

Тем не менее, определенно возможно await обещание, например await fooPromise().

  • Является ли async funtion отдельной вещью, а await просто совместим с промисами?

  • и есть ли способ отличить простой function от async function во время выполнения (совместимым с Babel способом)?


person Udo G    schedule 12.01.2016    source источник


Ответы (2)


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

function asyncFunc() {
  return doSomethingAsync() // doSomethingAsync() returns a promise
    .then(() => {
      // do some stuff
      return doSomethingElseAsync(); // returns a promise
    })
    .then(something => {
      // do some stuff
      return doSomethingElseEntirelyAsync(something); // returns a promise
    });
}

Превращается в

async function asyncFunc() {
  await doSomethingAsync(); // awaits for a promise
  // do some stuff
  let something = await doSomethingElseAsync(); // awaits for a promise
  // do some stuff
  return doSomethingElseEntirelyAsync(something); // returns the final promise
  // Note that even if you return a value, like return 5, the function as a whole
  // still returns a promise!
}

Он читается намного лучше, и вы можете использовать обычные инструменты, такие как try/catch и циклы for, для работы с ними, даже несмотря на то, что они асинхронны.

Асинхронные функции НЕ являются заменой промисов, они просто сахар поверх них, чтобы обрабатывать определенные случаи, когда у вас есть много последовательных асинхронных действий.

Поскольку await в основном просто «ждет этого промиса», вы все равно можете использовать классные методы агрегации, такие как Promise.all() и Promise.race(), и ждать результата нескольких (или первого из нескольких) промисов.

Я не знаком со способом их различения во время выполнения, потому что, как и классы, асинхронные функции — это просто сахар поверх промисов. (Хотя могут быть хаки, такие как использование функции .toString и анализ результатов, я их не считаю).

person Madara's Ghost    schedule 12.01.2016
comment
Спасибо, это имеет смысл. Так что, по сути, asyncFunc не является Обещанием, а asyncFunc() — или другими словами: typeof asyncFunc().then == "function" - person Udo G; 12.01.2016

Пара async/await представляет собой механизм, который позволяет вам писать асинхронный код в синхронном стиле, и, по моему скромному мнению, это самый простой и читаемый синтаксис для работы с асинхронным кодом (см. также этой статьи). Сила синтаксиса действительно в том, как работает await. Но чтобы использовать await внутри тела функции, функция должна иметь префикс async.

Если вам нужна дополнительная информация, есть спецификация для async/await здесь.

Текущая реализация в Babel 5 основана на https://github.com/facebook/regenerator. Как видно из транспилированный код функция компилируется в:

function asyncFunc(which, one, two) {
  return regeneratorRuntime.async(function asyncFuncMaybe$(context$1$0) {
...

Если вы покопаетесь в пакете Babel babel-regenerator-runtime, вы найдете код Facebook. В строка 205 вы найдете:

// Note that simple async functions are implemented on top of
// AsyncIterator objects; they just return a Promise for the value of
// the final result produced by the iterator.
runtime.async = function(innerFn, outerFn, self, tryLocsList) {
...

Чтобы транспилировать в ES5, async/await Babel нужно изменить код, чтобы мы могли отслеживать, где мы находимся во время выполнения функции, а AsyncIterator — это объект, который отслеживает это состояние.

Babel 6 предоставляет вам больше возможностей и позволяет выбрать реализацию, которую вы хотите использовать. См. Предложение Transpile Async Await с Babel.js?

Итак, что касается ваших вопросов:

  • async/await оба самостоятельны. Согласно спецификации, они должны работать с промисами. В частности, вы можете await выполнить обещание, и когда вы выполните функцию async, она вернет вам обещание.
  • Поскольку функция async транспилируется в функцию, возвращающую обещание, нет простого способа отличить ее от неасинхронной функции, возвращающей обещание. Ваш fooPromise должен больше походить на var fooPromiseFunc = function() {return new Promise(r => setTimeout(r, 1))};, что делает fooPromiseFunc и asyncFunc неотличимыми от предполагаемого черного ящика. Обе они являются функциями, которые возвращают обещание. По какой причине вы хотите различать async и неасинхронную функцию во время выполнения? На практике их можно использовать таким же образом, поэтому я не понимаю, почему вам придется угрожать им по-разному. В целях отладки, если вам действительно нужно выяснить, определена ли функция async, в Babel 5 вы можете использовать что-то вроде (asyncFunc+"").indexOf('regeneratorRuntime.async') > 0 или более точное регулярное выражение. Но это действительно хакерство, и я бы не стал использовать его вне контекста отладки или учебы.
person Chris Cinelli    schedule 12.01.2016
comment
Спасибо за подробное объяснение. Re: по какой причине вы хотите различать асинхронные и неасинхронные функции во время выполнения?: на самом деле я просто хотел отличить обычные обратные вызовы от промисов (в конце концов, чтобы сделать express промис/асинхронный- совместимы автоматически). Теперь, когда я знаю, как все работает, мне все ясно, и я смог создать отлично работающий (экспериментальный) патч для express. - person Udo G; 12.01.2016
comment
Для этого вам могут пригодиться эти 2 ссылки: strongloop.com /strongblog/ и github.com/luin/express-promise - person Chris Cinelli; 12.01.2016
comment
Спасибо за эти ссылки. Я знал их уже до публикации этого вопроса, и с помощью ответов я нашел способ избежать использования явной функции-оболочки и при этом получить точно такой же результат. - person Udo G; 13.01.2016