Недавно я начал изучать и немного экспериментировать с «новой» средой выполнения JavaScript / Typescript, Deno.
Дисклеймер/офф: и, кстати, я влюблена. Не только за то, что талисман — маленький динозавр, но и за всю скорость и легкость, которую мы получаем по сравнению с узлом. 🤪
Оценивая варианты выполнения задачи, предложенной Zanfranceschi, я наткнулся на Worker API. Я думал использовать его для вызова, но почувствовал, что это не лучший вариант решения проблемы.
В тот же день, когда я закончил hexchange, уже была начата новая задача: обработка содержимого файла распределенным способом. Это был бы «идеальный» сценарий для тестирования рабочих — хотя не обязательно распределенная обработка, но многопоточная.
Имея это в виду, я попытался придумать «простую» реализацию. Поехали! Но прежде чем я начну…
Как работники работают в Deno?
В двух словах, рабочие машины позволяют выполнять реализацию в нескольких различных потоках. Каждый экземпляр рабочего выполняется в выделенном для него потоке.
Для его выполнения необходимо иметь родителя и файл, который будет выполнять логику обработки, называемый рабочим.
Родитель
Родитель будет отвечать за создание логики для инстанцирования рабочих, а также за получение результатов их обработки.
Чтобы запросить выполнение рабочего, мы отправляем сообщение через метод postMessage
, которое должно содержать все данные, подлежащие обработке.
В случае с предложенной задачей мне нужно было запросить сумму строки с 200 числами, поэтому реализация была примерно такой:
const createWorkerURL = (filename: string): string => new URL(filename, import.meta.url).href;
export function sumDistributedFileNumbers(line: string, name: string): Promise<number> {
return new Promise((resolve) => {
const worker = new Worker(createWorkerURL('./workers.ts'), { type: 'module' });
worker.addEventListener('message', (message) => {
console.log(`Total of ${message.data.sum} returned from ${message.data.name}`);
resolve(message.data.sum);
});
worker.postMessage({ line, name });
});
}
В данном случае, помимо обычной реализации, я использовал еще две вещи: Promises
и метод addEventListener
из самого Worker.
В случае с обещанием мне нужно было использовать его, чтобы убедиться, что когда моя функция вернет что-то, она уже вернет сумму всех чисел в этом ряду. Метод addEventListener
, с другой стороны, я использовал для получения итоговой суммы строки — которая отправлялась через рабочий в родительский.
Обратите внимание, что для создания рабочих нам нужно передать ссылку на файл, в котором находится логика.
Рабочие
Следуя рассуждениям предложенной задачи, внутри рабочих я должен буду прочитать целую строку, содержащую в общей сложности 200 чисел. Чтобы добиться такого результата, такова была логика рабочего дела:
type MessageContent = {
line: string;
name: string;
};
self.onmessage = ({ data: { line, name } }: MessageEvent<MessageContent>): void => {
const sum = line
.split(' ')
.map((num) => Number(num))
.reduce((a, b) => {
return a + b;
});
self.postMessage({ sum, name });
self.close();
};
Таким образом, выполняется суммирование и, наконец, результат суммирования отправляется обратно родителю.
Окончательный результат
Для консолидации конечного результата, то есть всех частичных сумм, созданных рабочими, файл main.js
получает серию обещаний и выполняет их через метод Promise.all
. При успешном выполнении мы получим что-то вроде:
Total of 52499 returned from worker-1298
Total of 53023 returned from worker-1427
Total of 46830 returned from worker-1428
Total of 48380 returned from worker-1557
Total of 44707 returned from worker-1558
Total of 53450 returned from worker-1687
Total of 53031 returned from worker-1688
Total of 50385 returned from worker-1817
Total of 52455 returned from worker-1818
Total of 50101 returned from worker-1947
Total of 51728 returned from worker-1948
File total sum: 102367758!
Processing finished with 17589.7198 ms.
При сложности O(n²) приложению требуется в среднем ~18 секунд для выполнения суммы 20 000 строк файла, предполагая, что в каждой строке находится 200 чисел.
Следующим шагом будет выполнение ~de facto~ упражнения в соответствии с предпосылкой распределенной обработки и, наконец, сравнение его с этим упражнением.
Отказ от ответственности
Я хотел бы поблагодарить @zanfranceschi за то, что он вдохновил меня сделать первый шаг в создании контента с помощью своих задач! Большое спасибо! 😁
До следующего раза! 🤗