9
9
import { Importer , ImporterReturnType , Options , Result , SassException } from 'sass' ;
10
10
import { MessageChannel , Worker } from 'worker_threads' ;
11
11
12
+ /**
13
+ * The maximum number of Workers that will be created to execute render requests.
14
+ */
15
+ const MAX_RENDER_WORKERS = 4 ;
16
+
12
17
/**
13
18
* The callback type for the `dart-sass` asynchronous render function.
14
19
*/
@@ -19,6 +24,7 @@ type RenderCallback = (error?: SassException, result?: Result) => void;
19
24
*/
20
25
interface RenderRequest {
21
26
id : number ;
27
+ workerIndex : number ;
22
28
callback : RenderCallback ;
23
29
importers ?: Importer [ ] ;
24
30
}
@@ -39,9 +45,11 @@ interface RenderResponseMessage {
39
45
* the worker which can be up to two times faster than the asynchronous variant.
40
46
*/
41
47
export class SassWorkerImplementation {
42
- private worker ?: Worker ;
48
+ private readonly workers : Worker [ ] = [ ] ;
49
+ private readonly availableWorkers : number [ ] = [ ] ;
43
50
private readonly requests = new Map < number , RenderRequest > ( ) ;
44
51
private idCounter = 1 ;
52
+ private nextWorkerIndex = 0 ;
45
53
46
54
/**
47
55
* Provides information about the Sass implementation.
@@ -74,14 +82,23 @@ export class SassWorkerImplementation {
74
82
throw new Error ( 'Sass custom functions are not supported.' ) ;
75
83
}
76
84
77
- if ( ! this . worker ) {
78
- this . worker = this . createWorker ( ) ;
85
+ let workerIndex = this . availableWorkers . pop ( ) ;
86
+ if ( workerIndex === undefined ) {
87
+ if ( this . workers . length < MAX_RENDER_WORKERS ) {
88
+ workerIndex = this . workers . length ;
89
+ this . workers . push ( this . createWorker ( ) ) ;
90
+ } else {
91
+ workerIndex = this . nextWorkerIndex ++ ;
92
+ if ( this . nextWorkerIndex >= this . workers . length ) {
93
+ this . nextWorkerIndex = 0 ;
94
+ }
95
+ }
79
96
}
80
97
81
- const request = this . createRequest ( callback , importer ) ;
98
+ const request = this . createRequest ( workerIndex , callback , importer ) ;
82
99
this . requests . set ( request . id , request ) ;
83
100
84
- this . worker . postMessage ( {
101
+ this . workers [ workerIndex ] . postMessage ( {
85
102
id : request . id ,
86
103
hasImporter : ! ! importer ,
87
104
options : serializableOptions ,
@@ -96,7 +113,9 @@ export class SassWorkerImplementation {
96
113
* is only needed if early cleanup is needed.
97
114
*/
98
115
close ( ) : void {
99
- this . worker ?. terminate ( ) ;
116
+ for ( const worker of this . workers ) {
117
+ void worker . terminate ( ) ;
118
+ }
100
119
this . requests . clear ( ) ;
101
120
}
102
121
@@ -117,6 +136,7 @@ export class SassWorkerImplementation {
117
136
}
118
137
119
138
this . requests . delete ( response . id ) ;
139
+ this . availableWorkers . push ( request . workerIndex ) ;
120
140
121
141
if ( response . result ) {
122
142
// The results are expected to be Node.js `Buffer` objects but will each be transferred as
@@ -193,11 +213,13 @@ export class SassWorkerImplementation {
193
213
}
194
214
195
215
private createRequest (
216
+ workerIndex : number ,
196
217
callback : RenderCallback ,
197
218
importer : Importer | Importer [ ] | undefined ,
198
219
) : RenderRequest {
199
220
return {
200
221
id : this . idCounter ++ ,
222
+ workerIndex,
201
223
callback,
202
224
importers : ! importer || Array . isArray ( importer ) ? importer : [ importer ] ,
203
225
} ;
0 commit comments