Що це таке і як ним користуватися

Під час написання програм JavaScript ви могли стикатися з асинхронними функціями, такими як функція fetch у браузері або функція readFile у Nodejs.

Можливо, ви отримали несподівані результати, якщо використовували будь-яку з цих функцій, як зазвичай. Це тому, що вони є асинхронними функціями. У цій статті пояснюється, що це означає та як професійно використовувати асинхронні функції.

Вступ до синхронної функції

JavaScript — це однопотокова мова, яка може виконувати лише одну дію за раз. Це означає, що якщо процесор стикається з функцією, яка займає багато часу, JavaScript чекає, поки вся функція буде виконана, перш ніж перейти до інших частин програми.

Більшість функцій повністю виконується процесором. Це означає, що під час виконання зазначених функцій, незалежно від того, скільки часу це займає, процесор буде повністю зайнятий. Вони називаються синхронними функціями. Нижче наведено приклад синхронної функції:

function add(a, b) {
    for (let i = 0; i < 1000000; i ++) {
        // Do nothing
    }
    return a + b;
}

// Calling the function will take a long time
sum = add(10, 5);

// However, the processor cannot move to the 
console.log(sum);

Ця функція виконує великий цикл, виконання якого займає багато часу, перш ніж повернути суму своїх двох аргументів.

Після визначення функції ми викликали її та зберегли її результат у змінній sum. Далі ми записали значення змінної sum. Незважаючи на те, що виконання функції додавання займає деякий час, процесор не може перейти до реєстрації суми, доки виконання не завершиться.

Переважна більшість функцій, з якими ви зіткнетеся, поводитимуться передбачувано, як описано вище. Однак деякі функції є асинхронними і не поводяться як звичайні функції.

Вступ до асинхронної функції

Асинхронні функції виконують більшу частину своєї роботи поза процесором. Це означає, що, незважаючи на те, що виконання функції може зайняти деякий час, процесор буде вільним і вільним для виконання додаткової роботи.

Ось приклад такої функції:

fetch('https://jsonplaceholder.typicode.com/users/1');

Щоб підвищити ефективність, JavaScript дозволяє процесору переходити до інших завдань, які вимагають ЦП, ще до завершення виконання асинхронної функції.

Оскільки процесор перейшов до завершення виконання асинхронної функції, її результат не буде одразу доступним. Це буде в очікуванні. Якби процесор намагався виконати інші частини програми, які залежали від очікуваного результату, ми отримували б помилки.

Тому процесор повинен виконувати лише ті частини програми, які не залежать від очікуваного результату. Для цього сучасний JavaScript використовує обіцянки.

Що таке Promise у JavaScript?

У JavaScript обіцянка — це тимчасове значення, яке повертає асинхронна функція. Обіцянки є основою сучасного асинхронного програмування на JavaScript.

Після створення обіцянки відбувається одне з двох. Він або вирішується, коли значення, що повертається асинхронною функцією, успішно створено, або відхиляється у випадку помилки. Це події життєвого циклу обіцянки. Таким чином, ми можемо приєднати обробники подій до обіцянки, яка буде викликана, коли вона вирішить або відхилить.

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

Ось приклад, коли ми читаємо дані з файлу в Nodejs.

const fs = require('fs/promises');

fileReadPromise = fs.readFile('./hello.txt', 'utf-8');

fileReadPromise.then((data) => console.log(data));

fileReadPromise.catch((error) => console.log(error));

У першому рядку ми імпортуємо модуль fs/promises.

У другому рядку ми викликаємо функцію readFile, передаючи ім’я та кодування файлу, вміст якого ми хочемо прочитати. Ця функція є асинхронною; тому він повертає обіцянку. Ми зберігаємо обіцянку у змінній fileReadPromise.

У третьому рядку ми долучили слухач подій, коли обіцянка вирішується. Ми зробили це, викликавши метод then для об’єкта promise. Як аргумент для нашого виклику методу then ми передали функцію для запуску, якщо і коли обіцянка вирішується.

У четвертому рядку ми приєднали слухача, коли обіцянка відхиляється. Це робиться шляхом виклику методу catch і передачі обробника події помилки як аргументу.

Альтернативним підходом є використання ключових слів async і await. Далі ми розглянемо цей підхід.

Пояснення асинхронного та очікування

Ключові слова Async і Await можна використовувати для написання асинхронного Javascript у спосіб, який виглядає краще синтаксично. У цьому розділі я поясню, як використовувати ключові слова та який вплив вони мають на ваш код.

Ключове слово await використовується для призупинення виконання функції під час очікування завершення асинхронної функції. Ось приклад:

const fs = require('fs/promises');

function readData() {
	const data = await fs.readFile('./hello.txt', 'utf-8');

    // This line will not be executed until the data becomes available
	console.log(data);
}

readData()

Ми використали ключове слово await під час виклику readFile. Це вказувало процесору чекати, поки файл буде прочитано, перш ніж можна буде виконати наступний рядок (console.log). Це гарантує, що код, який залежить від результату асинхронної функції, не буде виконано, доки результат не стане доступним.

Якщо ви спробуєте запустити наведений вище код, ви зіткнетеся з помилкою. Це тому, що await можна використовувати лише всередині асинхронної функції. Щоб оголосити функцію асинхронною, ви використовуєте ключове слово async перед оголошенням функції таким чином:

const fs = require('fs/promises');

async function readData() {
	const data = await fs.readFile('./hello.txt', 'utf-8');

    // This line will not be executed until the data becomes available
	console.log(data);
}

// Calling the function so it runs
readData()

// Code at this point will run while waiting for the readData function to complete
console.log('Waiting for the data to complete')

Запустивши цей фрагмент коду, ви побачите, що JavaScript виконує зовнішній console.log, очікуючи, поки дані, зчитані з текстового файлу, стануть доступними. Коли він стає доступним, виконується console.log всередині readData.

Обробка помилок під час використання ключових слів async і await зазвичай виконується за допомогою спробувати/спіймати блоки. Також важливо знати, як цикл з асинхронним кодом.

Async і await доступні в сучасному JavaScript. Традиційно асинхронний код писався за допомогою зворотних викликів.

Знайомство зі зворотними викликами

Зворотний виклик — це функція, яка буде викликана, коли буде доступний результат. Весь код, який потребує повернення значення, буде розміщено всередині зворотного виклику. Все інше за межами зворотного виклику не залежить від результату і, отже, може бути виконано безкоштовно.

Ось приклад читання файлу в Nodejs.

const fs = require("fs");

fs.readFile("./hello.txt", "utf-8", (err, data) => {

	// In this callback, we put all code that requires 
	if (err) console.log(err);
	else console.log(data);
});

// In this part here we can perform all the tasks that do not require the result
console.log("Hello from the program")

У першому рядку ми імпортували модуль fs. Далі ми викликали функцію readFile модуля fs. Функція readFile буде читати текст із файлу, який ми вказуємо. Перший аргумент — це файл, а другий — формат файлу.

Функція readFile читає текст із файлів асинхронно. Для цього він приймає функцію як аргумент. Цей аргумент функції є функцією зворотного виклику та буде викликаний після прочитання даних.

Перший аргумент, переданий під час виклику функції зворотного виклику, — це помилка, яка матиме значення, якщо помилка виникне під час виконання функції. Якщо помилки не виявлено, він буде невизначеним.

Другим аргументом, який передається зворотному виклику, є дані, зчитані з файлу. Код усередині цієї функції матиме доступ до даних із файлу. Код поза цією функцією не потребує даних із файлу; тому його можна виконати під час очікування даних із файлу.

Виконання наведеного вище коду дасть такий результат:

Основні функції JavaScript

Є деякі ключові функції та характеристики, які впливають на роботу асинхронного JavaScript. Вони добре пояснені у відео нижче:

Нижче я коротко описав дві важливі функції.

#1. Однопотоковий

На відміну від інших мов, які дозволяють програмісту використовувати кілька потоків, JavaScript дозволяє використовувати лише один потік. Потік — це послідовність інструкцій, які логічно залежать одна від одної. Кілька потоків дозволяють програмі виконувати інший потік, коли зустрічаються операції блокування.

Однак кілька потоків ускладнюють і ускладнюють розуміння програм, які їх використовують. Це збільшує ймовірність появи помилок у коді, і буде важко налагодити код. JavaScript був зроблений однопоточним для простоти. Як однопотокова мова, вона покладається на керування подіями для ефективної обробки операцій блокування.

#2. Керований подіями

JavaScript також керується подіями. Це означає, що деякі події відбуваються протягом життєвого циклу програми JavaScript. Як програміст, ви можете приєднати функції до цих подій, і щоразу, коли подія відбудеться, приєднана функція буде викликана та виконана.

Деякі події можуть виникати через наявність результату операції блокування. У цьому випадку асоційована функція викликається з результатом.

Що слід враховувати під час написання асинхронного JavaScript

В цьому останньому розділі я згадаю деякі речі, які слід враховувати під час написання асинхронного JavaScript. Це включатиме підтримку веб-переглядача, найкращі практики та важливість.

Підтримка браузера

Це таблиця, яка показує підтримку промісів у різних браузерах.

Джерело: caniuse.com

У цій таблиці показано підтримку асинхронних ключових слів у різних браузерах.

Джерело: caniuse.com

Кращі практики

  • Завжди вибирайте async/await, оскільки це допомагає вам писати чистіший код, про який легко думати.
  • Обробка помилок у блоках try/catch.
  • Використовуйте ключове слово async лише тоді, коли необхідно дочекатися результату функції.

Важливість асинхронного коду

Асинхронний код дозволяє писати більш ефективні програми, які використовують лише один потік. Це важливо, оскільки JavaScript використовується для створення веб-сайтів, які виконують багато асинхронних операцій, таких як мережеві запити та читання або запис файлів на диск. Завдяки цій ефективності такі середовища виконання, як NodeJS, стали популярними як кращі середовища виконання для серверів додатків.

Заключні слова

Це була довга стаття, але в ній ми змогли розглянути, чим асинхронні функції відрізняються від звичайних синхронних функцій. Ми також розглянули, як використовувати асинхронний код, використовуючи лише обіцянки, ключові слова async/await і зворотні виклики.

Крім того, ми розглянули ключові функції JavaScript. В останньому розділі ми завершили, висвітливши підтримку браузера та передові практики.

Далі перегляньте поширені запитання Node.js для інтерв’ю.