Node Hero: Частина 3 - Поняття асинхронного програмування в Node.js

6 хв. читання

Синхронне програмування

У традиційній практиці програмування, більшість операцій вводу/виводу відбуваються синхронно. Наприклад, в Java ми б зчитували файл приблизно так:

try(FileInputStream inputStream = new FileInputStream("foo.txt")) {  
    Session IOUtils;
    String fileContent = IOUtils.toString(inputStream);
}

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

Якщо у вас є більше блокуючих операцій, то ситуація погіршується:

Node.js

(Червоні стовпчики показують, коли процес чекає відповіді від зовнішніх ресурсів і коли його заблоковано, чорні стовпчики показують, коли ваш код працює, зелені стовпчики показують іншу частину додатка)

Щоб вирішити цю проблему Node.js представив модель асинхронного програмування.

Асинхронне програмування в Node.js

Асинхронний ввід/вивід є формою I/O, що дає змогу продовжувати інші операції до того, як певна дія буде завершена.

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

Давайте почнемо з простого прикладу - зчитування файлу з використанням Node.js синхронним чином:

const fs = require('fs')  
let content  
try {  
  content = fs.readFileSync('file.md', 'utf-8')
} catch (ex) {
  console.log(ex)
}
console.log(content)  

Що тут відбувається? Ми намагалися зчитати файл, використовуючи синхронний інтерфейс модуля fs. Усе працює, як очікувалося - змінна content буде містити зміст file.md. Проблема такого підходу полягає в тому, що Node.js буде заблокований, поки операція не буде завершена - а це значить, що він не може абсолютно нічого зробити, поки файл зчитується.

Давайте подивимося, як ми можемо це виправити!

Асинхронне програмування - як ми знаємо з JavaScript - може бути досягнуто тільки з нативними функціями мови: вони можуть передаватися іншим функціям, як і будь-які інші змінні. Функції, які можуть приймати інші функції в якості аргументів, називаються функціями вищого порядку (higher-order functions).

Один з найпростіших прикладів функцій вищого порядку:

const numbers = [2,4,1,5,4]

function isBiggerThanTwo (num) {  
  return num > 2
}

numbers.filter(isBiggerThanTwo)  

У наведеному вище прикладі ми передаємо функцію функції filter. Таким чином, ми можемо визначити логіку фільтрації.

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

Ці так звані error-first callback'и знаходяться в самому серці самого Node.js - основні модулі використовують його так само, як і більшість модулів у NPM.

const fs = require('fs')  
fs.readFile('file.md', 'utf-8', function (err, content) {  
  if (err) {
    return console.log(err)
  }

  console.log(content)
})

Зверніть увагу:

  • обробка помилок: замість блокування try-catch вам слід перевірити на наявність помилок callback-функцію

  • не повертає значень: асинхронні функції не повертають значень, але значення будуть передані callback-функціям

Давайте трохи змінимо цей файл, щоб побачити, як це працює на практиці:

const fs = require('fs')

console.log('start reading a file...')

fs.readFile('file.md', 'utf-8', function (err, content) {  
  if (err) {
    console.log('error happened during reading the file')
    return console.log(err)
  }

  console.log(content)
})

console.log('end of the file')  

Вивід цього скрипту буде:

start reading a file...  
end of the file  
error happened during reading the file  

Як ви можете бачити, як ми почали читати наш файл реалізація команд все одно продовжується, і додаток вивів end of the file. Наша callback-функція була викликана тільки коли зчитування файлу було завершено. Як це можливо? Зустрічайте цикл подій (event loop).

Цикл подій

Цикл подій знаходиться в самому серці Node.js/JavaScript - він відповідає за планування асинхронних операцій.

Перш ніж занурюватися глибше, давайте розберемося, що таке event-driven-програмування.

Event-driven-програмування - це парадигма програмування, в якій потік програми визначається подіями, такими як дії користувача (клацання миші, натискання на клавіатуру), або повідомлень з інших програм/потоків.

На практиці, це означає, що додаток оперує подіями.

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

Щоб зрозуміти цикл подій глибше, подивіться це відео:

Асинхронне керування потоками

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

Async.js

Для того, щоб уникнути так званого Callback-пекла використовуйте async.js.

Async.js допомагає структурувати ваші додатки і робить управління потоками легше.

Наведемо короткий приклад використання Async.js, а потім перепишемо його, використовуючи Promises.

Наступний фрагмент коду проходиться по трьом файлам для статистики:

async.parallel(['file1', 'file2', 'file3'], fs.stat, function (err, results) {  
    // результат - масив статистики для кожного файлу
})

Promises

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

На практиці, попередній приклад може бути переписаний наступним чином:

function stats (file) {  
  return new Promise((resolve, reject) => {
    fs.stat(file, (err, data) => {
      if (err) {
        return reject (err)
      }
      resolve(data)
    })
  })
}

Promise.all([  
  stats('file1'),
  stats('file2'),
  stats('file3')
])
.then((data) => console.log(data))
.catch((err) => console.log(err))

Звичайно, якщо ви використовуєте метод, який має інтерфейс Promise, то Promise-приклад може бути ще коротше.

Далі: Частина 4 - Ваш перший Node.js HTTP сервер
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація