Skip to content

async await

garevna edited this page Jan 10, 2019 · 18 revisions

🎓 async | await

ES7

Иногда нужно синхронизировать несколько асинхронных операций

В ES7 это достигается легко с помощью асинхронной функции

Для объявления асинхронной функции используется ключевое слово async ( перед function )

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

Результатом будет объект класса 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 выполняет ту же роль, что и метод then() каждого промиса

Различие в том, что метод then() промиса запускает свой коллбэк асинхронно,

а ключевое слово await запускает функцию обратного вызова в нужное время

⚠️ Если бы сама асинхронная функция не была промисом, при ее вызове мы получили бы блокирующую операцию, что недопустимо

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

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

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

☕ 1️⃣

Асинхронные процессы
function common ( message ) {
    return new Promise (
        ( resolve, reject ) => {
            setTimeout (
                () => resolve ( message ),
                Math.random() * 8000
            )
        }
    )
}

function first () {
    return common ( "first" )
}

function second () {
    return common ( "second" )
}

function third () {
    return common ( "third" )
}

Задача - синхронизировать вызов коллбэков так,

чтобы первым в консоль было выведено "first", затем "second", а затем "third"

Аасинхронный код выводит их в случайном порядке:

асинхронный код
first().then( res => console.log ( res) )
second().then( res => console.log ( res) )
third().then( res => console.log ( res) )
цепочка промисов
first().then(
    res => {
        console.log ( res )
        second().then(
            res => {
                console.log ( res )
                third().then(
                    res => console.log ( res )
                )
            }
        )
    } 
)
асинхронная функция
let test = async () => {
    console.log ( await first() )
    console.log ( await second() )
    console.log ( await third() )
}

test()

Как видно из примера, код асинхронной функции проще, чем цепочка промисов


☕ 2️⃣

async function getUsersData ( userName ) {

    let readJSON = url => fetch ( url )
        .then ( response => response.json() )

    let userData = await readJSON (
        `https://api.github.com/users/${userName}`
    )

    document.body.appendChild (
        document.createElement ( "img" )
    ).src = userData.avatar_url

    let userRepos = await readJSON ( userData.repos_url )

    return readJSON ( userRepos[0].events_url )
}

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

☕ 3️⃣

Синхронизация асинхронных процессов приводит к увеличению времени выполнения

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

var getNames = () => 
    new Promise ( ( resolve, reject ) => 
        setTimeout (
            () => resolve ( "Names" ),
            1000
        )  
    )

var getPosts = () => 
    new Promise ( ( resolve, reject ) =>  
        setTimeout (
            () => resolve ( "Posts" ),
            1000
        )
    )

Каждый вызов длится 1 секунду

Если мы будем использовать асинхронную функцию для последовательного вызова getNames и getPosts, то суммарная продолжительность выполнения этих двух асинхронных операций составит не менее 2 сек

async function getData () {
    console.time ( 'time' )
    var posts = await getPosts ()
    var names = await getNames ()
    console.timeEnd ( 'time' )
    console.info ( `\n${ names } | ${ posts }\n\n` )
}

getData ()
Результат в консоли
Names | Posts

time: 2002.258056640625ms

Что плохо?

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

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

function getData () {
    console.time ( 'time' )
    Promise.all ([
        getNames (),
        getPosts ()
    ])
        .then (
            result => {
                console.info ( `\n${ result[0] } | ${ result[1] }\n\n` )
                console.timeEnd ( 'time' )
            }
        )
}
Результат в консоли
Names | Posts

time: 1001.474365234375ms

☕ 4️⃣

<body>
    <label for="user">Name of github user:</label>
    <input id="user"/>
</body>
function getData ( typ ) {
    return new Promise ( function ( resolve, reject ) {
        setTimeout ( () => {
            console.log ( 'Promise resolved: ', typ )
            resolve ( typ )
        }, 1000 )
    })
}

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

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

Функция getData () возвращает промис

Промис будет разрешен через 1 сек

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

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

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

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

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


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

☕ 5️⃣

© 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