Skip to content

async await

garevna edited this page Oct 26, 2018 · 18 revisions

🎓 async | await

ES7

Иногда в нашем коде инициирутся асинхронные операции, результат которых необходим для работы дальнейшего кода

В таком случае необходимо приостановить выполнение кода до завершения асинхронных операций и получения нужных данных

Для этого нужно объявить асинхронную функцию ( с ключевым словом async перед function ), а в теле функции перед каждым выражением, возвращающим промис, поставить ключевое слово await

Ключевое слово async используется для определения асинхронной функции

Результатом будет объект класса AsyncFunction

async function test () {}
console.dir ( test )
▼ async ƒ test()
    arguments: (...)
    caller: (...)
    length: 0
    name: "test"
  ▼ __proto__: AsyncFunction
        arguments: (...)
        caller: (...)
      ► constructor: ƒ AsyncFunction()
        Symbol(Symbol.toStringTag): "AsyncFunction"
      ▼ __proto__: ƒ ()
        ► apply: ƒ apply()
          arguments: (...)
        ► bind: ƒ bind()
        ► call: ƒ call()
          caller: (...)
        ► constructor: ƒ Function()
          length: 0
          name: ""
        ► toString: ƒ toString()
        ► Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
        ► get arguments: ƒ ()
        ► set arguments: ƒ ()
        ► get caller: ƒ ()
        ► set caller: ƒ ()
        ► __proto__: Object

⚠️ AsyncFunction не является глобальным объектом

Попытка отратиться к объекту AsyncFunction вызовет исключение:

test instanceof AsyncFunction
🚫 Uncaught ReferenceError: AsyncFunction is not defined

поэтому получить ссылку на нее можно, например, так:

var AsyncFunctionConstructor = test.__proto__.constructor

или:

var AsyncFunction = ( async function () {} ).__proto__.constructor

Теперь исключения не будет:

test instanceof AsyncFunction  // true

⚠️ Асинхронная функция всегда возвращает промис

Поэтому нельзя конструктор объявить как асинхронную функцию - конструктор должен возвращать экземпляр объекта, а не обещание


🎓 Принцип работы

Ключевое слово await приостанавливает выполнение кода до завершения асинхронной операции, запущенной выражением, следующим за await

⚠️ Ключевое слово await можно использовать только внутри асинхронных функций
В противном случае будет сгенерировано исключение

⛔️ Uncaught SyntaxError: await is only valid in async function

Предположим, мы загружаем данные с сервера из нескольких JSON-файлов

Эти данные нужны для построения страницы, чем и занимается дальнейший скрипт

В этом случае мы обычно вешаем коллбэки с помощью метода then промисов
Если промисов много, мы выстраиваем цепочку промисов...
В общем, наш код становится громоздким

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

☕ 1️⃣

async function getUsersData () {
    var promises = [
        fetch ( 'https://api.github.com/users/josh' ).then ( response => response.json() ),
        fetch ( 'https://api.github.com/users/heff' ).then ( response => response.json() ),
        fetch ( 'https://api.github.com/users/roland' ).then ( response => response.json() )
    ]
    return await Promise.all( promises )
}

getUsersData().then ( users => console.log ( users ) )

☕ 2️⃣

async function getUsersData ( userName ) {
    var userData = await fetch ( `https://api.github.com/users/${userName}` )
        .then ( response => response.json() )
    var userRepos = await fetch ( userData.repos_url )
        .then ( response => response.json() )
    return userRepos
}

getUsersData( 'josh' ).then ( repos => console.log ( repos ) )

☕ 3️⃣ Предположим, есть две функции, возвращающие промис:

var getNames = () => 
    new Promise ( ( resolve, reject ) => {
        setTimeout ( () => { resolve ( "Success" ) }, 1000 )  
    } )
var getPosts = () => 
    new Promise ( ( resolve, reject ) => { 
        setTimeout ( () => { resolve ( "Success" ) }, 1000 )
    } )

Каждый вызов длится 1 секунду
Нам нужно получить ответы от этих функций прежде, чем продолжить выполнение кода

Объявим асинхронную функцию:

async function getData () {
    console.time ( 'time' )
    var posts = await getPosts ().then ( response => response )
    var names = await getNames ().then ( response => response )
    console.timeEnd ( 'time' )
    console.info ( `names: ${ names } | posts: ${ posts }` )
}

Функция getData () фиксирует время начала операций ( в секундах )

затем вызовет функцию getPosts () с ключевым словом await,
что означает, что пока асинхронный процесс функции getPosts ()
не завершится тем или иным образом,
выполнение кода будет приостановлено

Итак, getData () ждет завершения асинхронной операции getPosts (),
которая длится 1 сек.
Когда операция будет завершена
( т.е. промис функции getPosts () будет разрешен либо resolve, либо reject ),
функция getData () вызовет функцию getNames (),
опять-таки с ключевым словом await
Опять ожидание ( 1 сек ) завершения асинхронного процесса
функции getNames (), после чего функция getData ()
выведет в консоль суммарную продолжительность всех операций
и полученные результаты

Теперь вызовем функцию getData ()

getData ()

и посмотрим в консоль

2 сек. names: Success  | posts: Success

Аналогичного результата мы могли достичь с помощью цепочки промисов

Что плохо?

То, что несвязанные между собой асинхронные процессы выстраиваются в очередь
Суммарная продолжительность операций 2 сек

Посмотрим на альтернативный вариант

☕ 4️⃣

function getData ( typ ) {
    return new Promise ( function ( resolve, reject ) {
        setTimeout ( () => {
            console.log ( 'Promise resolved: ', typ )
            resolve ( typ )
        }, 1000 )
    })
}

async function getAllData () {
    console.time ( "Total" )
    let promises = Array.from ( arguments )
        .map ( x => getData ( x ) )
    await Promise.all ( promises )
         .then ( response => {
             console.timeEnd ( "Total" )
             console.log ( "response: ", response )
          })
}

getAllData ( "figures", "colors", "diameters" )

Функция getData () возвращает промис
Промис будет разрешен через 1 сек

Асинхронная функция getAllData ()
формирует массив промисов promises
и запускает сразу все асинхронные процессы
с помощью метода Promise.all ()

Перед вызовом метода Promise.all () стоит ключевое слово await`

Что происходит в этом случае:

Мы не выстраиваем очередь, а запускаем сразу все асинхронные процессы параллельно

Общая продолжительность операции не будет суммой продолжительности всех асинхронных процессов

В нашем примере вместо 3 секунд, которые мы получили бы в случае последовательной обработки запросов ( как в примере 1 ) мы получили общую продолжительность 1 сек

Используем fetch для получения данных трех типов: JSON, text, img

☕ 5️⃣

☕ 6️⃣

function getData ( typ ) {
    var __data = {
        colors: [ "blue", "green", "yellow", "orange", "red" ],
        figures: [ "triangle", "circle", "elipse", "square" ],
        diameters: [ 100, 200, 150, 250, 300 ]
    }
    return new Promise ( function ( resolve, reject ) {
        setTimeout ( () => {
            console.log ( 'Promise resolved: ', typ )
            Object.keys ( __data ).indexOf ( typ ) < 0 
                    ? reject ( "Data error" ) 
                    : resolve ( __data [ typ ] )
        }, Math.floor ( 2000 * Math.random() ) )
    })
}

async function getAllData () {
    let promises = []
    for ( var x of arguments ) {
        promises.push ( getData ( x ) )
    }
    return res = await Promise.all ( promises )
}

getAllData ( "figures", "colors", "diameters" )
    .then ( response => console.log ( 'response: ', response ) )

© Irina H.Fylyppova 2018
Использование данных материалов или любой их части коммерческими школами ( курсами ) является нарушением авторских прав


Новая версия


1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19

Занятие 1

⤵️

Занятие 2

⤴️ ⤵️

Занятие 3

⤴️ ⤵️

Занятие 4

⤴️ ⤵️

Занятие 5

⤴️ ⤵️

Занятие 6

⤴️ ⤵️

Занятие 7

⤴️ ⤵️

Занятие 8

⤴️ ⤵️

Занятие 9

⤴️ ⤵️

Занятие 10

⤴️ ⤵️

Занятие 11

⤴️ ⤵️

Занятие 12

⤴️ ⤵️

Занятие 13

⤴️ ⤵️

Занятие 14

⤴️ ⤵️

Занятие 15

⤴️ ⤵️

Занятие 16

⤴️ ⤵️

Занятие 17

⤴️ ⤵️

Занятие 18

⤴️ ⤵️

Занятие 19

⤴️ ⤵️

⤴️

ico20 Дополнительно
dir-20 Справочная инфо

Clone this wiki locally