UNPKG

3.95 kBJavaScriptView Raw
1'use strict';
2
3function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
4
5var _require = require('events');
6
7const EventEmitter = _require.EventEmitter;
8
9const os = require('os');
10const Farm = require('worker-farm/lib/farm');
11const promisify = require('./utils/promisify');
12const logger = require('./Logger');
13
14let shared = null;
15
16class WorkerFarm extends Farm {
17 constructor(options) {
18 let opts = {
19 maxConcurrentWorkers: getNumWorkers()
20 };
21
22 let workerPath = parseInt(process.versions.node, 10) < 8 ? require.resolve('../lib/worker') : require.resolve('../src/worker');
23
24 super(opts, workerPath);
25
26 this.localWorker = this.promisifyWorker(require('./worker'));
27 this.remoteWorker = this.promisifyWorker(this.setup(['init', 'run']));
28
29 this.started = false;
30 this.warmWorkers = 0;
31 this.init(options);
32 }
33
34 init(options) {
35 this.localWorker.init(options);
36 this.initRemoteWorkers(options);
37 }
38
39 promisifyWorker(worker) {
40 let res = {};
41
42 for (let key in worker) {
43 res[key] = promisify(worker[key].bind(worker));
44 }
45
46 return res;
47 }
48
49 initRemoteWorkers(options) {
50 var _this = this;
51
52 return _asyncToGenerator(function* () {
53 _this.started = false;
54 _this.warmWorkers = 0;
55
56 let promises = [];
57 for (let i = 0; i < _this.options.maxConcurrentWorkers; i++) {
58 promises.push(_this.remoteWorker.init(options));
59 }
60
61 yield Promise.all(promises);
62 if (_this.options.maxConcurrentWorkers > 0) {
63 _this.started = true;
64 }
65 })();
66 }
67
68 receive(data) {
69 if (data.event) {
70 this.emit(data.event, ...data.args);
71 } else if (data.type === 'logger') {
72 if (this.shouldUseRemoteWorkers()) {
73 logger.handleMessage(data);
74 }
75 } else if (this.children[data.child]) {
76 super.receive(data);
77 }
78 }
79
80 shouldUseRemoteWorkers() {
81 return this.started && this.warmWorkers >= this.activeChildren;
82 }
83
84 run(...args) {
85 var _this2 = this;
86
87 return _asyncToGenerator(function* () {
88 // Child process workers are slow to start (~600ms).
89 // While we're waiting, just run on the main thread.
90 // This significantly speeds up startup time.
91 if (_this2.shouldUseRemoteWorkers()) {
92 return _this2.remoteWorker.run(...args, false);
93 } else {
94 // Workers have started, but are not warmed up yet.
95 // Send the job to a remote worker in the background,
96 // but use the result from the local worker - it will be faster.
97 if (_this2.started) {
98 _this2.remoteWorker.run(...args, true).then(function () {
99 _this2.warmWorkers++;
100 }, function () {
101 // ignore error
102 });
103 }
104
105 return _this2.localWorker.run(...args, false);
106 }
107 })();
108 }
109
110 end() {
111 // Force kill all children
112 this.ending = true;
113 for (let child in this.children) {
114 this.stopChild(child);
115 }
116
117 this.ending = false;
118 shared = null;
119 }
120
121 static getShared(options) {
122 if (!shared) {
123 shared = new WorkerFarm(options);
124 } else {
125 shared.init(options);
126 }
127
128 return shared;
129 }
130}
131
132for (let key in EventEmitter.prototype) {
133 WorkerFarm.prototype[key] = EventEmitter.prototype[key];
134}
135
136function getNumWorkers() {
137 if (process.env.PARCEL_WORKERS) {
138 return parseInt(process.env.PARCEL_WORKERS, 10);
139 }
140
141 let cores;
142 try {
143 cores = require('physical-cpu-count');
144 } catch (err) {
145 cores = os.cpus().length;
146 }
147 return cores || 1;
148}
149
150module.exports = WorkerFarm;
\No newline at end of file