Await

У програмуванні, await — особливість, яка була впроваджена у C# 5.0, C++20, Python 3.5, Hack, Dart, Kotlin 1.1, експериментальному доповненні для Scala[1], «нічній» збірці Rust[2], і в нещодавніх версіях JavaScript. Вона дозволяє здійснити виклик асинхронного методу схожим чином, як викликаються звичайні, синхронні методи.

Базові операції

Все, що роблять ключові слова async та await, є коригуванням коду під час компіляції. Для цього використовується клас Task, який зберігає стан операції.

Приклад коду, в якому використовується await:

public async Task<int> FindPageSize(Uri uri)
{
    var data = await new WebClient().DownloadDataTaskAsync(uri);
    return data.Length;
}

Тут завантажується URI та повертається його довжина. Ключове слово «async» вказує компілятору, що можна переорганізувати код всередині методу, щоб дозволити відкладати відмічені виклики й обгортати їхні результати в Task. Результатом функції FindPageSize є об'єкт типу Task<int> — це означає, що функція повертає ціле число, але воно може бути неготовим для використання, щойно керування повернулося з FindPageSize — на нього потрібно «зачекати».

Тому компілятор може скорегувати цей код до такого вигляду:

public Task<int> FindPageSize(Uri uri)
{
    var data_task = new WebClient().DownloadDataTaskAsync(uri);
    var after_data_task = data_task.ContinueWith((original_task) => {
        return original_task.Result.Length;
    });
    return after_data_task;
}

async у сигнатурі методу дозволяє корегування коду з використанням Task і ContinueWith при кожному використанні await. Компілятор не змінить код, якщо await не використовується (C# видає попередження), а метод не буде асинхронним.

Task можна використовувати незалежно від await. «await» є синтаксичним цукром для спрощення роботи кінцевого користувача. Тобто, при кожному використанні await решта коду, що використовує результат асинхронної функції, обгортається у блок ContinueWith.

Також, в асинхронному методі не обов'язково використовувати await. При виклику асинхронної функції без використання await задача виконуватиметься у фоновому режимі. Змінну типу Task, яку повернула функція, можна використовувати для отримання інформації про стан завдання. Таким чином, можна запустити кілька асинхронних задач одночасно, зберегти їх у списку й отримати результат, передавши його до Task.WhenAll() . Це дозволяє їм усім виконуватися одночасно, а потім просто очікує на виконання всіх задач. Це може бути набагато ефективніше за очікування завершення кожної з них. Наприклад, виконуючи 10 мережевих запитів, якщо розпочати їх усі одночасно (а не послідовно, використовуючи await), а потім очікувати виклик WhenAll, це може призвести до паралельного виконання всіх мережевих запитів.

В С#

До сьомої версії С# асинхронні методи обов'язково мають повертати void, Task, або Task<T>. Потім цей список поповнився іншими типами на кшталт ValueTask<T>. Асинхронні методи, які нічого не повертають, призначені для обробників подій. У більшості випадків результат асинхронного методу, який нічого не повертає, рекомендується позначати як Task, а не void, так як це сприяє інтуїтивному керуванню виключеннями[3].

Сигнатуру методів, що використовують await, треба позначати за допомогою async. У методах, які повертають змінні типу Task<T> та позначені асинхронними, має повертатися змінна типу T, а не Task<T>. Компілятор обгортає значення в Task<T>. Також передбачена можливість виклику функцій, які повертають задачу, через await, навіть якщо вони не позначені асинхронними.

Наведений нижче асинхронний метод, завантажує дані із URL використовуючи await.

public async Task<int> SumPageSizesAsync(IList<Uri> uris)
{
    int total = 0;
    foreach (var uri in uris) {
        statusText.Text = string.Format("Found {0} bytes ...", total);
        var data = await new WebClient().DownloadDataTaskAsync(uri);
        total += data.Length;
    }
    statusText.Text = string.Format("Found {0} bytes total", total);
    return total;
}

В C++

В C++, await (який називається co_await в C++) був офіційно долучений до стандарту C++20[4]; також компілятори MSVC та Clang вже підтримують co_await.

В JavaScript

Оператор await в JavaScript може використовуватися лише в функції, що відмічена ключовим словом async. Якщо параметр є Promise-ом, виконання async-функції відновиться, коли Promise буде вирішено(resolved). Якщо Promise буде відхилий(rejected) - в цьому випадку буде викинута помилка, яка може бути оброблена з використанням звичайного сценарію виключення JavaScript. Якщо параметр не є Promise-ом, сам параметр буде повернуто негайно.[5]

Багато бібліотек надають повертають Promise-об'єкти, які також можуть використовуватися в очікуванні, якщо вони узгоджуються з специфікацією для власних Promise-об'єктів JavaScript. Проте обіцянки з бібліотеки jQuery не були сумісними з власними Promis-об'єктами на рівні A+ аж до версії jQuery 3.0.[6]

Приклад (взято з цієї статті[7]):

async function createNewDoc() {
  let response = await db.post({}); // post a new doc
  return await db.get(response.id); // find by id
}

async function main() {
  try {
    let doc = await createNewDoc();
    console.log(doc);
  } catch (err) {
    console.log(err);
  }
}()



Примітки

  1. scala/scala-async. GitHub (англійською). Процитовано 18 квітня 2018.
  2. alexcrichton/futures-await. GitHub (англійською). Процитовано 18 квітня 2018.
  3. Async/Await - Best Practices in Asynchronous Programming. MSDN (англійською). Процитовано 18 квітня 2018.
  4. ISO C++ Committee announces that C++20 design is now feature complete.
  5. await - JavaScript (MDN). Процитовано 2 травня 2017.
  6. jQuery Core 3.0 Upgrade Guide. Процитовано 2 травня 2017.
  7. Taming the asynchronous beast with ES7. Процитовано 12 листопада 2015.
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.