概述

Web Workers是一种Web标准技术,允许在后台线程中执行脚本处理。 WijmoJS 的2018v3版本引入了Web Workers技术,以便在生成PDF时提高应用程序的运行速度。

一般来说,JavaScript脚本在主线程中执行,由同一个线程处理UI呈现。因此繁重的JavaScript处理可能会阻止UI呈现,轻者会导致用户无法与屏幕交互; 在最严重的情况下,UI绘图过程停止太长时间,浏览器会抛出异常警告。

传统方式:在没有Web Worker的情况下处理PDF

在下面的动画中,您将看到我们以传统方式将100,000行数据保存到PDF。 主线程运行了很长时间,同时,UI冻结,我们看到了异常警告提示。(这段动画是以4倍的速度播放的)

Processing a PDF without Web Workers

在这种情况下,用户无法取消PDF的生成过程。 此外,只要UI被冻结,我们就无法更改按钮的状态或显示其他有用的信息,比如进度情况。 用户只能等待很长时间,造成不满意的用户体验。

全新方式:利用Web Workers改善用户体验

Web Workers解决了上述问题。 以下是Web Workers在处理相同数据量的表现:

Process a PDF using Web Workers

虽然繁重的数据处理仍然需要很长时间,但用户可以随时取消处理(从而避免异常警告)。 此外,我们可以更改按钮状态并显示当前进度,为用户提供重要的反馈。

Web Workers示例

让我们看看下面的示例,该应用程序将压缩100,000个表格数据到3,500页的PDF 中 - 这是一个体积庞大的文档。 (我建议在Chrome上运行它。)

要正常执行Web Worker,请创建一个定义Web Worker进程的JavaScript文件。 然后读取JS文件,并执行Web Worker。 为了更好地体验,请务必在WijmoJS 2018 v3中下载本示例。

HTML:

<div class="container">

  <h1>Web Worker</h1>
  <p>Generate PDF documents in the background.</p>
  <p>By using Web Workers, we can generate PDF documents in a separate thread from the UI thread. This allows the application to be usable while the PDF is generated in the background. Otherwise, the application would freeze until the PDF is generated. For large documents, this could be a minute or even longer.</p>
  <label for="rowCount">Number of Rows</label>
  <select id="rowCount">
    <option value="10000">10,000</option>
    <option value="100000">100,000</option>
  </select><br>
  <button id="saveButton1" class="btn btn-default">Create PDF(using Web Worker)</button>&nbsp;
  <button id="saveButton2" class="btn btn-default">Create PDF(without Web Worker)</button><br>
  <div id="progressGauge"></div>
  <span id="progressText">0</span>% Done
  <div id="flexGrid"></div>

</div>

JavaScript:

var productNames = "Chai,Chang,Guaraná Fantástica,Sasquatch Ale,Steeleye Stout,Côte de Blaye,Chartreuse verte,Ipoh Coffee,Laughing Lumberjack Lager,Outback Lager,Rhönbräu Klosterbier,Lakkalikööri,Aniseed Syrup,Chef Anton's Cajun Seasoning,Chef Anton's Gumbo Mix,Grandma's Boysenberry Spread,Northwoods Cranberry Sauce,Genen Shouyu,Gula Malacca,Sirop d'érable,Vegie-spread,Louisiana Fiery Hot Pepper Sauce,Louisiana Hot Spiced Okra,Original Frankfurter grüne Soße,Pavlova,Teatime Chocolate Biscuits,Sir Rodney's Marmalade,Sir Rodney's Scones,NuNuCa Nuß-Nougat-Creme,Gumbär Gummibärchen,Schoggi Schokolade,Zaanse koeken,Chocolade,Maxilaku,Valkoinen suklaa,Tarte au sucre,Scottish Longbreads,Queso Cabrales,Queso Manchego La Pastora,Gorgonzola Telino,Mascarpone Fabioli,Geitost,Raclette Courdavault,Camembert Pierrot,Gudbrandsdalsost,Fløtemysost,Mozzarella di Giovanni,Gustaf's Knäckebröd,Tunnbröd,Singaporean Hokkien Fried Mee,Filo Mix,Gnocchi di nonna Alice,Ravioli Angelo,Wimmers gute Semmelknödel,Mishi Kobe Niku,Alice Mutton,Thüringer Rostbratwurst,Perth Pasties,Tourtière,Pâté chinois,Uncle Bob's Organic Dried Pears,Tofu,Rössle Sauerkraut,Manjimup Dried Apples,Longlife Tofu,Ikura,Konbu,Carnarvon Tigers,Nord-Ost Matjeshering,Inlagd Sill,Gravad lax,Boston Crab Meat,Jack's New England Clam Chowder,Røgede sild,Spegesild,Escargots de Bourgogne,Röd Kaviar".split(",");
var rowCount = document.getElementById('rowCount');
var saveButton1 = document.getElementById('saveButton1');
var saveButton2 = document.getElementById('saveButton2');
var progressGauge = new wijmo.gauge.LinearGauge('#progressGauge', {
  value: 0
});
var progressText = document.getElementById('progressText');
var isExporting = false;

var flexGrid = new wijmo.grid.FlexGrid('#flexGrid', {
  itemsSource: orders(rowCount.value)
});
flexGrid.columns[1].width = 200;
rowCount.addEventListener('change', function() {
  flexGrid.collectionView.sourceCollection = orders(rowCount.value);
});

// Create web worker
var cdn = 'https://demo.grapecity.com/wijmo/beta/controls/';
var script =
  "importScripts('" + cdn + "wijmo.min.js');" +
  "importScripts('" + cdn + "wijmo.pdf.min.js');" +
  "importScripts('" + cdn + "wijmo.grid.pdf.min.js');" +
  "wijmo.grid.pdf.PdfWebWorker.initExportGrid();";
var blob = new Blob([script], { type:'text/javascript' });
var worker;

// Create PDF using Web Worker
saveButton1.addEventListener('click', function() {
  if (!isExporting) {
    // Execute PDF generation processing
    isExporting = true;
    saveButton1.innerText = 'Cancel';
    saveButton2.disabled = true;
    worker = new Worker(URL.createObjectURL(blob));
    wijmo.grid.pdf.PdfWebWorkerClient.exportGrid(worker, flexGrid, 'FlexGrid.pdf', null, done, progress);
  } else {
    // Cancel PDF generation process
    worker.terminate();
    progress(0);
    isExporting = false;
    saveButton1.innerText = 'Create PDF(using Web Worker)';
    saveButton2.disabled = false;
  }
});

// Callback function executed when PDF creation is completed
function done() {
  isExporting = false;
  saveButton1.innerText = 'Create PDF(using Web Worker)';
  saveButton2.disabled = false;
}

// Callback function executed during PDF creation
function progress(value) {
  value = Math.floor((value * 100));
  progressText.innerText = value;
  progressGauge.value = value;
}

// Create PDF without using Web Worker
saveButton2.addEventListener('click', function() {
  saveButton1.disabled = true;
  saveButton2.innerText = 'Cancel';
  wijmo.grid.pdf.FlexGridPdfConverter.export(flexGrid, 'FlexGrid.pdf');
  saveButton1.disabled = false;
  saveButton2.innerText = 'Create PDF(without Web Worker)';
});

function orders(length) {
  var orders = [];
  var _years = 4;
  for (var i = 0; i < length; i++) {
    var date = new Date((new Date()).getFullYear() - _years, 3, 1);
    orders.push({
      id: i,
      product: productNames[random(productNames.length)],
      date: wijmo.DateTime.addDays(date, random(365 * _years)),
      amount: random(200, 10),
      discount: random(3) == 0
    });
  }
  return orders;
}

function random(max, min) {
  if (!min) min = 0;
  return Math.floor(Math.random() * (max - min)) + min;
}

CSS:

#flexGrid {
  height: 300px;
  margin-top: 20px;
}
#progressGauge {
  width: 400px;
  display: inline-block;
  vertical-align: middle;
}
#progressText {
  width: 30px;
  display: inline-block;
  text-align: right;
}
#saveButton1, #saveButton2 {
  width: 230px;
}
</style>
<link rel="stylesheet" href="https://demo.grapecity.com/wijmo/beta/styles/wijmo.min.css" />
<script src="https://demo.grapecity.com/wijmo/beta/controls/wijmo.min.js"></script>
<script src="https://demo.grapecity.com/wijmo/beta/controls/wijmo.pdf.min.js"></script>
<script src="https://demo.grapecity.com/wijmo/beta/controls/wijmo.grid.min.js"></script>
<script src="https://demo.grapecity.com/wijmo/beta/controls/wijmo.grid.pdf.min.js"></script>
<style>

Result:

Web Worker 对于 JavaScript应用程序的重要作用

当JavaScript执行繁重的处理时,用户体验可能会大大减少,但是对于Web Workers来说,时间的感觉通过得更快。 使用Web Workers对于在Web应用程序中执行繁重的处理至关重要。现在WijmoJS 已经将这项技术引入,请大家免费试用!