#javascript #typescript #asynchronous #libuv
#javascript #typescript #асинхронный #libuv
Вопрос:
Я использую Typescript и, следовательно libuv
, для выполнения любых операций ввода-вывода. В моем конкретном сценарии я беру хэш отпечатка данного файла. Чтобы подчеркнуть здесь мой вопрос, рассмотрите входной файл как файл объемом 1 ТБ. Чтобы получить отпечаток файла, я мог бы открыть файл через поток файлов и обновить хэш:
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha256');
const fh = fse.createReadStream(filepath, {
highWaterMark : 100000000
});
fh.on('data', (d) => { hash.update(d); });
fh.on('end', () => {
resolve(hash);
});
fh.on('error', reject);
});
Приведенный выше пример довольно медленный, учитывая его последовательный подход. Итак, более быстрый подход, о котором я думал, состоит в том, чтобы разделить вычисления на N блоков следующим образом:
let promises = [];
for (let i = 0; i < N; i) {
promises.push(calculateFilePart(file, from, to));
}
return Promise.all(all);
Учитывая приведенный выше пример, представьте N
, что это 1000000, означает ли это libuv
, что одновременно запускается 1000000 асинхронных операций ввода-вывода в фоновом режиме? Или libuv автоматически ставит их в очередь пакетами, чтобы избежать перенасыщения запросов ввода-вывода?
Любая помощь в этой теме высоко ценится!
Ответ №1:
Я попытаюсь как можно короче изложить некоторые ключевые концепции. Я оставлю ссылки для справки ниже, чтобы вы могли проверить факты.
Обещание добавляет задачу в нечто, называемое очередью микрозадач. На каждой итерации цикла событий, когда стек вызовов пуст, обрабатываются задачи из очереди микрозадач. Это известно как tick
. Таким образом, каждый тик будет обрабатываться несколько задач из очереди микрозадач.
Для каждого тика процесса существует значение max depth ( process.maxTickDepth
). Это указывает количество задач, которые должны быть выгружены из очереди микрозадач и помещены в стек вызовов.
Основная часть вашего алгоритма включает в себя чтение содержимого, которое является операцией ввода-вывода. Такие операции помещаются в отдельную очередь, известную как очередь макрозадач. Когда операция макрозадачи расписания завершена и у нее есть указанный фрагмент содержимого, обработчик события для операции чтения помещается в очередь в очередь микрозадач для обработки на следующем тике.
Учитывая ваш фрагмент и ограничения, если максимальная глубина равна 1000, то для вашего алгоритма для полного обновления хэша необходимо передать как минимум N / 1000 = 1000000 / 1000 = 1000
тики. Это означает, что Node.js процесс будет обрабатывать только определенное количество задач за тик.
Я надеюсь, что это дает вам понимание, которое вы ищете.
Ссылки:
Node.js Under The Hood #3 — Глубокое погружение в цикл событий
Комментарии:
1. Спасибо! Это потрясающее понимание! Я просмотрю ваши ссылки, поэтому, если я вас правильно понимаю,
libuv
буду обрабатывать макрозадачи самостоятельно, поэтому не о чем беспокоиться, если я выполню несколькоasync readFile(..)
операций,libuv
поскольку это просто ограничит истинное параллельное выполнение внутренними ограничениями и будет выполнять их пакетно. Я правильно резюмировал это?2. Я думаю, вы это сделали. Мое собственное понимание и знание всех материалов, с которыми я сталкивался до сих пор, предполагает это.