1- import MarkdownIt from 'markdown-it' ;
2- import * as nunjucks from 'nunjucks' ;
31import hash from 'object-hash' ;
42
5- import EvalWorker from '/js/workers/eval ?worker' ;
3+ import TemplatingWorker from '/js/workers/templating-worker ?worker' ;
64
7- import api from '/js/core/api' ;
85import store from '/js/core/store' ;
96
10- // DataImportExtension
7+ // Worker Pool
118//
12- // This nunjucks extension makes it possible to parse
13- // and add JSON to the context. This can help to embed
14- // static data into the templates.
15- function DataImportExtension ( ) {
16- this . tags = [ 'data' ] ;
17-
18- this . parse = function ( parser , nodes ) {
19- let tok = parser . nextToken ( ) ;
20-
21- let args = parser . parseSignature ( null , true ) ;
22- parser . advanceAfterBlockEnd ( tok . value ) ;
23-
24- let body = parser . parseUntilBlocks ( 'enddata' ) ;
25- parser . advanceAfterBlockEnd ( ) ;
26-
27- return new nodes . CallExtension ( this , 'run' , args , [ body ] ) ;
28- } ;
29-
30- this . run = function ( context , name , body ) {
31- try {
32- context . ctx [ name ] = JSON . parse ( body ( ) ) ;
33- } catch ( e ) {
34- console . log ( e ) ;
9+ //
10+ let cache = { } ;
11+ let workerSelect = 0 ;
12+ let workerPromises = { } ;
13+ let workers = new Array ( navigator . hardwareConcurrency || 4 ) . fill ( null ) . map ( ( _ , i ) => {
14+ let worker = new TemplatingWorker ( ) ;
15+
16+ // when rendered response is received call the related resolve or reject.
17+ worker . onmessage = ( e ) => {
18+ if ( e . data . log ) {
19+ console . log ( `Template Web-Worker ${ i + 1 } : ${ e . data . log } ` ) ;
20+ return ;
3521 }
36- return '' ;
37- } ;
38- }
39-
40- function JavascriptExecuteExtension ( ) {
41- this . tags = [ 'js' ] ;
42-
43- this . parse = function ( parser , nodes ) {
44- let tok = parser . nextToken ( ) ;
45-
46- let args = parser . parseSignature ( null , true ) ;
47- parser . advanceAfterBlockEnd ( tok . value ) ;
48-
49- let body = parser . parseUntilBlocks ( 'endjs' ) ;
50- parser . advanceAfterBlockEnd ( ) ;
5122
52- return new nodes . CallExtensionAsync ( this , 'run' , args , [ body ] ) ;
53- } ;
54-
55- this . run = function ( context , name , fn , callback ) {
56- let worker = new EvalWorker ( ) ;
23+ let { resolve, reject, timeout, hashed } = workerPromises [ e . data . id ] ;
24+ let res = e . data ;
5725
58- // Kill worker after timeout. This is important if
59- // the code has a infinite loop.
60- let timeout = setTimeout ( ( ) => {
61- worker . terminate ( ) ;
62- callback ( 'eval worker: timeout' ) ;
63- } , 1000 ) ;
26+ // stop timeout handler
27+ clearTimeout ( timeout ) ;
6428
65- // Wait for response.
66- worker . onmessage = ( e ) => {
67- clearTimeout ( timeout ) ;
68- worker . terminate ( ) ;
69- context . ctx [ name ] = e . data ;
70- callback ( null , '' ) ;
71- } ;
29+ if ( res . err ) {
30+ let parsedErr = parseError ( res . err ) ;
31+ cache [ hashed ] = parsedErr ;
32+ reject ( parsedErr ) ;
33+ } else {
34+ cache [ hashed ] = res . res ;
35+ resolve ( res . res ) ;
36+ }
7237
73- // Send request.
74- worker . postMessage ( [ context . ctx , fn ( ) ] ) ;
38+ delete workerPromises [ res . id ] ;
7539 } ;
76- }
77-
78- let env = new nunjucks . Environment ( ) ;
79- let markdown = new MarkdownIt ( ) ;
80-
81- env . addExtension ( 'DataImportExtension' , new DataImportExtension ( ) ) ;
82- env . addExtension ( 'JavascriptExecuteExtension' , new JavascriptExecuteExtension ( ) ) ;
83-
84- env . addFilter ( 'markdown' , ( md ) => new nunjucks . runtime . SafeString ( markdown . render ( md ) ) ) ;
85- env . addFilter ( 'markdowni' , ( md ) => new nunjucks . runtime . SafeString ( markdown . renderInline ( md ) ) ) ;
86- env . addFilter ( 'json' , ( data ) => new nunjucks . runtime . SafeString ( JSON . stringify ( data ) ) ) ;
87- env . addFilter (
88- 'source' ,
89- ( source , cb ) => {
90- let found = store . data . sources . find ( ( s ) => `ds:${ s . author } +${ s . slug } ` === source ) ;
9140
92- if ( ! found ) {
93- cb ( null , 'not found' ) ;
94- return ;
95- }
96-
97- api
98- . getEntries ( source )
99- . then ( ( res ) => cb ( null , res ) )
100- . catch ( ( err ) => cb ( err , null ) ) ;
101- } ,
102- true
103- ) ;
41+ return worker ;
42+ } ) ;
10443
10544// Exports
10645//
@@ -117,28 +56,28 @@ export const parseError = (e) => {
11756 return null ;
11857} ;
11958
120- let cache = { } ;
121-
12259export const render = ( template , state ) => {
12360 state . settings = store . data . settings ;
12461
12562 return new Promise ( ( resolve , reject ) => {
126- let id = hash ( template ) + hash ( state ) ;
127- if ( cache [ id ] ) {
63+ // check if data is present in cache
64+ let hashed = hash ( template ) + hash ( state ) ;
65+ if ( cache [ hashed ] ) {
12866 console . log ( 'templating: cache hit' ) ;
129- resolve ( cache [ id ] ) ;
67+ resolve ( cache [ hashed ] ) ;
13068 return ;
13169 }
13270
133- env . renderString ( template , state , ( err , res ) => {
134- if ( err ) {
135- let parsedErr = parseError ( err ) ;
136- cache [ id ] = parsedErr ;
137- reject ( parsedErr ) ;
138- } else {
139- cache [ id ] = res ;
140- resolve ( res ) ;
141- }
142- } ) ;
71+ // setup promises for response
72+ let id = hash + '-' + Math . ceil ( Math . random ( ) * 10000000 ) . toString ( ) ;
73+ workerPromises [ id ] = {
74+ hashed,
75+ resolve,
76+ reject,
77+ timeout : setTimeout ( ( ) => reject ( 'timeout' ) , 2000 ) ,
78+ } ;
79+
80+ // post message (round-robin style) to some worker
81+ workers [ workerSelect ++ % workers . length ] . postMessage ( { id, template, state } ) ;
14382 } ) ;
14483} ;
0 commit comments