运行 "background task" javascript

running a "background task" in javascript

是否有可能,在 Javascript 中,到 运行 一个后台函数?

我正在 angularJS 应用程序中使用 pdfmake 工具生成 pdf,但 pdf 生成时间为 quite 长(3-4 秒),在此期间,ui完全冻结。

我想 运行 一个后台任务并强制下载 pdf 而不 冻结用户 ui,这可能吗?

我是这样的 运行ning pdfmake(pdfmake_ 是自定义工厂):

'use strict';

angular.module('App')

    .service('CatalogPdfService', ['pdfmake', '_', '$q', '$filter',
        function (pdfmake, _, $q, $filter) {

            var $translate = $filter('translate');
            var listDate = new Date();

            return {
                download: download
            };

            function download(data) {

                listDate = _.first(data).publishedOn;
                console.log('start download');
                var deferred = $q.defer();
                var filename = $translate('APP.EXPORT.pdf.catalog.title', {date: $filter('amDateFormat')(listDate, 'DDMMYYYY')}) + '.pdf';
                create(data).download(filename, function () {
                    console.log('end download');
                    deferred.resolve();
                });
                return deferred.promise;
            }

            function create(data) {

                // group data by category
                var dataByCategory = _.groupBy(data, function (d) {
                    return d.category;
                });

                // group categories data by subcategory
                _.forEach(dataByCategory, function (d, i) {
                    dataByCategory[i] = _.groupBy(d, function (d) {
                        return d.subcategory;
                    });
                });

                var content = {
                    table: {
                        headerRows: 1,
                        widths: ['*', 20, 10, 20, 20, 20, 20, 40, 20, 30],
                        body: [
                            [
                                {text: $translate('APP.EXPORT.pdf.catalog.header.article')      , style: 'headings', alignment: 'left'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.mine')         , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.rank')         , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.origin')       , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.transporter')  , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.culture')      , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.label')        , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.unit')         , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.packing')      , style: 'headings'},
                                {text: $translate('APP.EXPORT.pdf.catalog.header.price')        , style: 'headings'}
                            ]
                        ]
                    },
                    layout: {
                        hLineWidth: function (i) {
                            return (i == 0) ? 0 : 1;
                        },
                        vLineWidth: function (i) {
                            return 0;
                        },
                        hLineColor: function (i, node) {
                            return '#ccc';
                        }
                    }
                };

                _.forEach(dataByCategory, function (data, category) {
                    content.table.body = content.table.body.concat(renderCategory(category, data));
                });

                var dd = {};
                dd.content = renderHeader().concat(content);
                dd.header = function (currentPage, pageCount) {
                    return {
                        text: $translate('APP.EXPORT.pdf.catalog.pagecount', {start: currentPage.toString(), end: pageCount.toString()}),
                        alignment: 'right',
                        color: '#666',
                        margin: [0, 20, 40, 0]
                    };
                };
                dd.styles = {
                    title: {
                        fontSize: 15,
                        bold: true
                    },
                    headings: {
                        italics: true,
                        alignment: 'center'
                    },
                    flag: {
                        alignment: 'center',
                        italics: true,
                        color: '#666'
                    },
                    category: {
                        bold: true,
                        fontSize: 12,
                        margin: [0, 10, 0, 0] // Left, Top, Right, Bottom
                    },
                    subcategory: {
                        bold: true,
                        fontSize: 10,
                        margin: [0, 7, 0, 5] // Left, Top, Right, Bottom
                    }
                };

                dd.defaultStyle = {
                    fontSize: 8
                };

                return pdfmake.createPdf(dd);
            }

            function renderHeader() {
                return [
                    {image: logo(), height:40, width: 86},
                    {
                        margin: [0, 10, 0, 20],
                        table: {
                            widths: [100, 100, 100, '*'],
                            body: [
                                [
                                    {text: $translate('APP.COMMON.address', {char: '\n'})},
                                    {text: '\n' + $translate('APP.COMMON.phone')},
                                    {text: '\n' + $translate('APP.COMMON.fax')},
                                    {text: '\n' + $translate('APP.EXPORT.pdf.catalog.listno', {date: $filter('amDateFormat')(listDate, 'DD/MM/YYYY')}) , alignment: 'right'}
                                ]
                            ]
                        },
                        layout: {
                            hLineWidth: function (i) {
                                return (i == 0) ? 0 : 1;
                            },
                            vLineWidth: function (i) {
                                return 0;
                            }
                        }
                    }];
            }

            function renderCategory(name, data) {

                var category = [
                    [
                        {text: name, style: 'category', colspan: 10},
                        '', '', '', '', '', '', '', '', ''
                    ]
                ];

                _.forEach(data, function (data, name) {
                    category = category.concat(renderSubcategory(name, data));
                });

                return category;
            }

            function renderSubcategory(name, data) {

                var subcategory = [
                    [
                        {text: name, style: 'subcategory', colspan: 10},
                        '', '', '', '', '', '', '', '', ''
                    ]
                ];

                _.forEach(data, function (product) {
                    subcategory.push(renderProduct(product));
                });

                return subcategory;
            }

            function renderProduct(product) {
                return [
                    product.name,
                    {
                        text: (product.isInPrivateList ? 'Oui' : ''),
                        style: 'flag'
                    },
                    {
                        text: (null === product.rank ? '' : String(product.rank)),
                        style: 'flag'
                    },
                    {
                        text: (product.origin || ''),
                        style: 'flag'
                    },
                    {
                        text: (product.transporter || ''),
                        style: 'flag'
                    },
                    {
                        text: (product.label || ''),
                        style: 'flag'
                    },
                    {
                        text: (product.culture || ''),
                        style: 'flag'
                    },
                    {
                        text: product.unit,
                        margin: [0, 0, 5, 0],
                        italics: true,
                        alignment: 'right'
                    },
                    {
                        text: (product.quantity || '1'),
                        italics: true,
                        fillColor: '#eee',
                        alignment: 'center'
                    },
                    {
                        text: product.unitPrice,
                        margin: [0, 0, 5, 0],
                        italics: true,
                        fillColor: '#eee',
                        alignment: 'right'
                    }
                ];
            }

            function logo() {
                return 'data:image/jpeg;base64,blabla bigbase64 string'
            }
        }]);

您可以使用 Web Worker 生成 PDF。但是您在使用它们时应该注意一些限制。 Here 是一个很好的参考。

我在 Angular 中创建了一个工厂用于在工作线程上工作。像这样:

/*
Here's an example on how to get this sack of moldering spuds to do something:

 var myWorker = new MyWorker({ fn: function() {
    this.onmessage = function(args) {
        setTimeout(function() {
            this.postMessage('Got args: ' + args.data);
        }, 20000);
    };
 } });

 myWorker.do('Test').then(function(message) {
    alert(message);
 });
 */

'use strict';

angular.module('myApp')
    .factory('MyWorker', function($q) {
        var _worker;

        var MyWorker = function(settings) {
            _init(settings);
        };

        MyWorker.prototype.do = function(args) {
            var deferred = $q.defer();

            _worker.onmessage = function(message) {
                deferred.resolve(message.data);
            };

            //Fire up the blades.
            if (args)
                _worker.postMessage(args);
            else
                _worker.postMessage();

            return deferred.promise;
        };

        MyWorker.prototype.destroy = function() {
            _worker.terminate();
        };

        function _init(settings) {
            if (settings.script)
                _worker = new Worker(settings.script);
            //Need to make this IE (10+) friendly.
            else if (settings.fn) {
                var blobUrl = window.URL.createObjectURL(new Blob(
                    ['(', settings.fn.toString(), ')()'],
                    { type: 'application/javascript' }
                ));

                _worker = new Worker(blobUrl);
            }
        };

        return MyWorker;
    });

这会让您大致了解如何在 AngularJS 中实现它,但请认真对待它。