Встроенные дочерние процессы в Node.js

Я знаком с веб-воркерами в браузере и решил, что эквивалент Node.js выполнения

const worker = new Worker('./worker')

является

const worker = child_process.fork('./worker')

API немного отличается, и они не совсем так же работают под капотом, но, в конце концов, веб-рабочие и дочерние процессы, похоже, делают примерно одно и то же; позволяют запускать код JavaScript параллельно.

Теперь есть эта изящная вещь, которую можно сделать с помощью веб-воркеров, когда вместо создания воркера путем передачи пути / URL-адреса файла, содержащего код воркера, вы можете передать фактическую функцию. Этого можно добиться с помощью этой простой трехстрочной функции:

function createWorker(fn) {
  var blob = new Blob(['self.onmessage = ', fn.toString()], { type: 'text/javascript' });
  var url = URL.createObjectURL(blob);

  return new Worker(url);
}

Это позволяет вам писать свой рабочий код встроенным в файл main / master / parent (как бы вы его ни называли), например:

var myWorker = createWorker(function (e) {
  self.postMessage(e.data.toUpperCase());
});

myWorker.onMessage = function (e) {
  console.info(e.data); // HELLO FROM AN INLINE WORKER!
}

myWorker.postMessage('hello from an inline worker!')

ВОПРОС
Как добиться того же с помощью дочерних процессов Node.js? Глядя на документацию, я не мог сказать, можно ли передать в child_process.fork что-то другое, кроме пути к модулю, или есть ли другой способ добиться того, чего я хочу; писать встроенные дочерние процессы.

ОБНОВИТЬ
Я пробовал следующее, основываясь на предложении @Bergi в комментариях:

const fileSync = require('tmp').fileSync;
const writeFileSync = require('fs').writeFileSync;
const fork = require('child_process').fork;

function createWorker(fn) {
  const tmpobj = fileSync();
  writeFileSync(tmpobj.name, fn.toString()); 

  return fork(tmpobj.name);
}

var myWorker = createWorker(function (e) {
  process.send(e.toUpperCase());
});

myWorker.on('message', function (e) {
  console.info(e); // HELLO FROM AN INLINE WORKER!
})

myWorker.send('hello from an inline worker!');

Увы, я получаю следующую ошибку:

C:\Users\phili\AppData\Local\Temp\tmp-165566BgqUKKM5yjR.tmp:1
(function (exports, require, module, __filename, __dirname) { function (e) {
                                                                       ^

SyntaxError: Unexpected token (
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:616:28)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)
    at bootstrap_node.js:609:3

Боюсь, я здесь не в себе. Есть идеи, что происходит и как я могу заставить его работать?

интересный вопрос. ИМХО, я не думаю, что все, что реализовано во фронтенде JS (как Web Worker), также работает в Backend JS (NodeJS).

Kai 15.05.2018 18:52

«где вы можете передать фактическую функцию» - не совсем, вы все еще передаете строку. Ты просто создать строку из тела функции - это хак.

Bergi 15.05.2018 19:46
var n = require("tmp").tmpNameSync(); require("fs").writeFileSync(n, fn.toString(); require("child_process").fork(n) должен иметь примерно такой же эффект
Bergi 15.05.2018 19:50

@Bergi, конечно, ты прав. Я попробую ваше решение.

snowfrogdev 15.05.2018 20:17

@Bergi Я обновил вопрос после того, как попробовал ваше предложение, но получаю странную синтаксическую ошибку. Взгляните, как вы думаете, что здесь происходит?

snowfrogdev 15.05.2018 21:38

Создается файл с буквальным содержанием function (e) { process.send(e.toUpperCase()); }. Это недопустимый сценарий с его объявлением функции без имени. Подобно тому, как взломать веб-воркер, помещая код функции в положение, в котором он интерпретируется как устанавливаемый обработчик сообщений, нам нужен способ запустить код. IIFE может это сделать (writeFileSync("("+fn.toString()+")();");), но вы можете дать ему некоторую среду выполнения (подключив его непосредственно к IPC или около того).

Bergi 15.05.2018 22:27

О, и не забудьте установить прослушиватель завершения в дочернем процессе, который удалит временный файл сценария. Кроме того, отказ от ответственности: это взлом и может быть дырой в безопасности - не следует запускать исполняемый код из каталога tmp, по крайней мере, без тщательного управления правами доступа к файлам.

Bergi 15.05.2018 22:33
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
7
886
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот что в итоге сработало для меня:

const fileSync = require('tmp').fileSync;
const writeFileSync = require('fs').writeFileSync;
const fork = require('child_process').fork;



function createWorker(fn) {
  const tmpobj = fileSync();
  writeFileSync(tmpobj.name, `process.on('message', ${fn.toString()})`); 

  return fork(tmpobj.name);
}

var myWorker = createWorker(function (e) {
  process.send(e.toUpperCase());
});

myWorker.on('message', function (e) {
  console.info(e); // HELLO FROM AN INLINE WORKER!
})

myWorker.send('hello from an inline worker!');

Миллион спасибо @Bergi за его помощь в выяснении этого вопроса.

С worker_threads теперь есть реальная возможность создавать потоки с помощью встроенного кода без необходимости во временных файлах (как показано Snowfrogdev).

const { Worker } = require("worker_threads");
new Worker(`console.info(require("process").env.HOME)`, { eval: true });

В этом примере установка eval на true также позволяет использовать встроенные функции nodejs.

Другие вопросы по теме