Современный асинхронный JavaScript с Async и Await

Откройте для себя современный подход к асинхронным функциям в JavaScript. JavaScript за очень короткое время эволюционировал от обратных вызовов до обещаний, а с ES2017 асинхронный JavaScript стал еще проще с синтаксисом async / await.

Вступление

JavaScriptэволюционировал за очень короткое время от обратных вызовов дообещания(ES2015), а так какES2017асинхронный JavaScript еще проще с синтаксисом async / await.

Асинхронные функции представляют собой комбинацию обещаний игенераторы, и, по сути, они представляют собой абстракцию более высокого уровня по сравнению с обещаниями. Повторяю:async / await построен на обещаниях.

Почему были введены async / await?

Они сокращают количество шаблонов вокруг обещаний и ограничение «не разрывайте цепочку», связанное с цепочкой обещаний.

Когда промисы были представлены в ES2015, они должны были решить проблему с асинхронным кодом, и они это сделали, но за 2 года, которые разделяли ES2015 и ES2017, стало ясно, чтообещания не могут быть окончательным решением.

Обещания были даны для решения знаменитыхобратный вызов адпроблема, но они сами по себе внесли сложность и сложность синтаксиса.

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

Они заставляют код выглядеть синхронным, но за кулисами он асинхронный и неблокирующий.

Как это устроено

Асинхронная функция возвращает обещание, как в этом примере:

const doSomethingAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 3000)
  })
}

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

const doSomething = async () => {
  console.log(await doSomethingAsync())
}

Быстрый пример

Это простой пример использования async / await для асинхронного запуска функции:

const doSomethingAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 3000)
  })
}

const doSomething = async () => { console.log(await doSomethingAsync()) }

console.log(‘Before’) doSomething() console.log(‘After’)

Приведенный выше код выведет в консоль браузера следующее:

Before
After
I did something //after 3s

Promise all the things

Prepending the async keyword to any function means that the function will return a promise.

Even if it’s not doing so explicitly, it will internally make it return a promise.

This is why this code is valid:

const aFunction = async () => {
  return 'test'
}

aFunction().then(alert) // This will alert ‘test’

and it’s the same as:

const aFunction = async () => {
  return Promise.resolve('test')
}

aFunction().then(alert) // This will alert ‘test’

The code is much simpler to read

As you can see in the example above, our code looks very simple. Compare it to code using plain promises, with chaining and callback functions.

And this is a very simple example, the major benefits will arise when the code is much more complex.

For example here’s how you would get a JSON resource, and parse it, using promises:

const getFirstUserData = () => {
  return fetch('/users.json') // get users list
    .then(response => response.json()) // parse JSON
    .then(users => users[0]) // pick first user
    .then(user => fetch(`/users/${user.name}`)) // get user data
    .then(userResponse => userResponse.json()) // parse JSON
}

getFirstUserData()

And here is the same functionality provided using await/async:

const getFirstUserData = async () => {
  const response = await fetch('/users.json') // get users list
  const users = await response.json() // parse JSON
  const user = users[0] // pick first user
  const userResponse = await fetch(`/users/${user.name}`) // get user data
  const userData = await userResponse.json() // parse JSON
  return userData
}

getFirstUserData()

Multiple async functions in series

Async functions can be chained very easily, and the syntax is much more readable than with plain promises:

const promiseToDoSomething = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 10000)
  })
}

const watchOverSomeoneDoingSomething = async () => { const something = await promiseToDoSomething() return something + ’ and I watched’ }

const watchOverSomeoneWatchingSomeoneDoingSomething = async () => { const something = await watchOverSomeoneDoingSomething() return something + ’ and I watched as well’ }

watchOverSomeoneWatchingSomeoneDoingSomething().then(res => { console.log(res) })

Will print:

I did something and I watched and I watched as well

Easier debugging

Debugging promises is hard because the debugger will not step over asynchronous code.

Async/await makes this very easy because to the compiler it’s just like synchronous code.


More js tutorials: