简体   繁体   中英

Is it possible to make an async renderToString with worker_threads?

I have been thinking about how to optimize react server side rendering, especially the portion of my app that is the bottleneck, which is the synchronous renterToString invocation. Because this is a blocking call, having long function renderToString invocations causes throughput of my application to suffer quite a bit.

I happened upon a thread (here its translated from Chinese to English with google translate) that brings up a hypothetical to use worker_threads to offload renderToString and make it async.

https://translate.google.com/translate?hl=en&sl=zh-CN&u=https://cnodejs.org/topic/5b2e596557137f22415c4e63&prev=search

I wanted to test this functionality and it seems like there is a blocker in that a Symbol cannot be passed as data to the worker because it cannot be cloned:

DataCloneError: Symbol(react.element) could not be cloned.

Here is example code for setting up the worker thread/server rendering logic:

// asyncRenderer.js
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
  module.exports = async function asyncRenderToString(component) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: component,
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0) {
          reject(new Error(`Worker stopped with exit code ${code}`));
        }
      });
    });
  };
} else {
  const { renderToString } = require('react-dom/server');
  const HTML = renderToString(workerData);
  parentPort.postMessage(HTML);
}


// server.jsx
const asyncRenderer = require('./asyncRenderer');
...
const html = await renderToString(<App/>);
// Return the html

I'd love to hear if it is possible to use worker_threads for this purpose, and if so, what the best approach for setting up the renderer/worker threads would be.

I'd love to hear if it is possible to use worker_threads for this purpose, and if so, what the best approach for setting up the renderer/worker threads would be.

First of all in general worker_threads is suited for CPU intensive work (like this) where parallelism and shared memory is helpful.

In this particular case simply spinning multiple Node.js processes would be equally effective.

Moreover, since ReactDOMServer renders pretty slowly - you almost always have to put a cache in front of server-side rendering in React (or Vue or Angular). So that's a much easier approach to improving performance.

As for how you'd use worker_threads here, it fails because you pass the component to it. Instead I'd pass the route or component name to it and have it require that component instead.

Alternatively, you can serialize the component yourself and deseiralize it on the other side. Symbols require uniqueness so we don't clone them by default when moving things between workers in Node.js. This is the error you're seeing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM