3

I am using worker thread and stream at same time in node JS project. At initial I was not able to pass res object through main process to worker thread. I saw many stackoverflow question and solution and wrote a solution which works great. I created a Readable stream in main thread and writable stream in worker thread. while doing this, I have done a huge calculation from more than 10 table and export data which takes nearly 1 minutes for processing. code:

router.get("/downloadAll", (req, res) => {
  new Promise((resolve, reject) => {
    const promise = [];
     promise.push(Dashboard.DUser());
  promise.push(Dashboard.DDUser());
  promise.push(Dashboard.DDLUser());
  promise.push(Dashboard.Din());
  promise.push(Dashboard.Str());
  promise.push(Dashboard.R());
  promise.push(Dashboard.Q());
  Promise.all(promise).catch(err => err)
  .then(results => {  

    const worker = new Worker(`${process.cwd()}/src/route/modules/dashboard/worker.js`, {
          workerData: { results }
        });
        const fileHeaders = [
          {
            name: "Content-Type",
            value: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
          }
        ];

        res.setHeader("Content-Disposition", `attachment; filename="Stream.xlsx`);
        fileHeaders.forEach(header => res.setHeader(header.name, header.value));

        const readStream = new Readable({
          read() {}
        });

        readStream.pipe(res);
        worker.on("message", message => {
          readStream.push(message);
        });

        worker.on("exit", code => {
          console.log("exit", code);
          resolve(true);
          //if (code !== 0) reject(new Error(`stopped with  ${code} exit code`));
        });
      });
  })
    .then(() => res.end())
    .catch(err => console.log(err));
});

WORKER THREAD:

const { workerData, parentPort } = require("worker_threads");
const { Writable } = require("stream");

const Excel = require("exceljs");

const writableStream = new Writable();
// writableStream.on("message", () => {});
writableStream._write = (chunk, encoding, next) => {
  parentPort.postMessage(chunk);
  next();
};
const createWorkbook = () => {
  const workbook = new Excel.stream.xlsx.WorkbookWriter({
    stream: writableStream, // stream to server response
    useStyles: true // not sure about this one, check with it turned off.
  });

  workbook.title = "Serious";
  workbook.creator = "SS";
  workbook.created = new Date();
  return workbook;
};
const createSheet = workbook => {
  workerData.results.forEach((result, index) => {
    const worksheet = workbook.addWorksheet(result.title, {
      properties: { outlineLevelCol: 1 }
    });
    worksheet.columns = Object.keys(result.data[0]).map(item => {
      return { header: item, key: item };
    });

    result.data.forEach(row => worksheet.addRow(row).commit);
  });
};

const workbook = createWorkbook();
createSheet(workbook);
workbook.commit();

The above code works fine and is fast for small calculation. when I have huge computation it is showing processing for 1 minutes and finish processing and download the xls file. so i updated the code to:

router.get("/downloadAll", (req, res) => {
  const worker = new Worker(`${process.cwd()}/src/worker/worker.js`);
  const fileHeaders = [
    {
      name: "Content-Type",
      value: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    }
  ];
  const today = new Date();

  res.setHeader(
    "Content-Disposition",
    `attachment; filename=Q-${today.getFullYear()}${String(today.getMonth() + 1).padStart(2, "0")}${String(
      today.getDate()
    ).padStart(2, "0")}.xlsx`
  );
  fileHeaders.forEach(header => res.setHeader(header.name, header.value));

  const readStream = new Readable({
    read() {}
  });

  readStream.pipe(res);
  worker.on("message", message => {
    readStream.push(message);
  });

  worker.on("exit", code => {
    console.log("exit", code);
    res.end();
    //if (code !== 0) reject(new Error(`stopped with  ${code} exit code`));
  });
});

and worker thread code:

    const { workerData, parentPort } = require("worker_threads");
const { Writable } = require("stream");
const Excel = require("exceljs");
const { resolve } = require("path");
const db = require(`${process.cwd()}/src/modules/db.module`);
const Dashboard = require(`${process.cwd()}/src/route/modules/dashboard.model`);
const promise = [];
     promise.push(Dashboard.DUser());
  promise.push(Dashboard.DDUser());
  promise.push(Dashboard.DDLUser());
  promise.push(Dashboard.Din());
  promise.push(Dashboard.Str());
  promise.push(Dashboard.R());
  promise.push(Dashboard.Q());
  Promise.all(promise).catch(err => err)
  .then(results => {   const writableStream = new Writable();
    // writableStream.on("message", () => {});
    writableStream._write = (chunk, encoding, next) => {
      console.log(chunk.toString());
      parentPort.postMessage(chunk);
      next();
    };
    const createWorkbook = () => {
      const workbook = new Excel.stream.xlsx.WorkbookWriter({
        stream: writableStream, // stream to server response
        useStyles: true // not sure about this one, check with it turned off.
      });

      workbook.creator = "ss";
      workbook.created = new Date();
      return workbook;
    };

    const createSheet = workbook => {
      results.forEach((result, index) => {
        // console.log(result);
        const worksheet = workbook.addWorksheet(result.title, {
          properties: { outlineLevelCol: 1 }
        });
        worksheet.columns = Object.keys(result.data[0]).map(item => {
          return { header: item, key: item };
        });

        result.data.forEach(row => worksheet.addRow(row).commit);
      });
    };

The above code doesnot work correctly. I can get the data from callback from promise but when its downloading its shows 300kb , 200b,1byte and ends to 0 but it does not download.

if I try to insert the promise inside createsheet then i am getting error:

Error [ERR_UNHANDLED_ERROR]: Unhandled error. ({ message: 'queue closed', code: 'QUEUECLOSED', data: undefined })

code:

const createSheet = workbook => {
  let promise = [];
  /**
   * get count of all the user list
   */

    promise.push(Dashboard.DDPro());

    Promise.all(promise)

      .then(results => {
        results.forEach((result, index) => {
          console.log(result);
          const worksheet = workbook.addWorksheet(result.title, {
            properties: { outlineLevelCol: 1 }
          });
          worksheet.columns = Object.keys(result.data[0]).map(item => {
            return { header: item, key: item };
          });

          result.data.forEach(row => worksheet.addRow(row).commit);
        });
      })
      .catch(err => console.log(err));
};

can any body helps me solve the problem.

0 Answers0