Попытаемся выполнить несколько асинхронных вызовов одновременно и затем воспользоваться их результатом. Попробуем переписать нашу задачу по объединению файлов. Вот её текст:
Предположим, что перед нами стоит задача прочитать содержимое двух файлов и записать в третий (объединение файлов)
Из постановки видно, что оба исходных файла можно прочитать одновременно и затем, когда они оба будут прочитаны, записать новый файл.
fs.readFile('./first', 'utf-8', (_error1, data1) => {
// ?
});
fs.readFile('./second', 'utf-8', (_error2, data2) => {
// ?
});
Так как наш код асинхронный, результат работы каждой функции можно получить лишь внутри колбеков. Причём, порядок запуска колбеков мы не можем знать — всё зависит от того, какой файл прочитается быстрее. Для отслеживания состояния выполнения этих операций придётся ввести глобальное состояние (относительно этих операций), через которое мы будем отслеживать завершённость и в котором сохраним данные. И только когда все операции завершились — запишем новый файл. Кроме того, нам нужно чётко разделять данные первого и второго файлов, так как запись в новый файл (в отличие от чтения) должна происходить в определённом порядке.
const state = {
count: 0,
results: [],
};
fs.readFile('./first', 'utf-8', (_error1, data1) => {
state.count += 1;
state.results[0] = data1;
});
fs.readFile('./second', 'utf-8', (_error2, data2) => {
state.count += 1;
state.results[1] = data2;
});
Когда обе операции завершатся, состояние заполнится данными, а значение count
станет 2
. Именно на это условие мы и завяжем наш код:
import fs from 'fs';
const state = {
count: 0,
results: [],
};
const tryWriteNewFile = () => {
if (state.count !== 2) {
return; // guard expression
}
fs.writeFile('./new-file', state.results.join(''), (error) => {
if (error) {
return;
}
console.log('finished!');
});
};
console.log('first reading was started');
fs.readFile('./first', 'utf-8', (error1, data1) => {
console.log('first callback');
if (error1) {
return;
}
state.count += 1;
state.results[0] = data1;
tryWriteNewFile();
});
console.log('second reading was started');
fs.readFile('./second', 'utf-8', (error2, data2) => {
console.log('second callback');
if (error2) {
return;
}
state.count += 1;
state.results[1] = data2;
tryWriteNewFile();
});
// Один запуск
// node index.js
// first reading was started
// second reading was started
// second callback
// first callback
// finished!
// Другой запуск
// node index.js
// first reading was started
// second reading was started
// first callback
// second callback
// finished!
Теперь файлы читаются параллельно и мы, наконец-то, увидели на практике преимущество одновременного выполнения асинхронных операций. Скорость выполнения этой программы значительно выше синхронного варианта! Причём, чем больше размер файлов, тем больше разница. Однако, стоит заметить, что, хотя чтение файлов происходит параллельно, работа самого js, обрабатывающего результат — строго последовательна. Колбеки начинают запускаться только после того, как опустеет текущий стек вызовов, и в том порядке, в котором завершились асинхронные операции (параллельный запуск не означает, что операции заканчиваются и начинаются одновременно).
Каждый раз писать подобный код очень утомительно, поэтому лучше воспользоваться библиотекой под названием async, которая предоставляет набор готовых абстракций для работы в асинхронном стиле. Она содержит десятки функций для большого числа задач, связанных с упорядочиванием асинхронных операций. Ниже пример решения нашей задачи с использованием этой библиотеки:
import { map } from 'async';
import fs from 'fs';
map(['./first', './second'], fs.readFile, (err1, results) => {
if (err1) {
return;
}
fs.writeFile('./new-file', results.join(''), (err2) => {
if (err2) {
return;
}
console.log('finished!');
});
});
Согласитесь, что это значительно лучше ;) Но, как увидите позже, можно пойти еще дальше.
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт