在Node.js中为异步的多个请求运行多个幻像进程

时间:2016-11-29 17:05:52

标签: node.js asynchronous phantomjs

我正在编写一个node.js应用程序,它允许用户在node.js中生成多页pdf。

我逐个创建页面并将各个页面作为一个数组传递,其中包含从函数到函数的请求,直到创建所有页面,此时我使用pdf merge将多页文档放在一起。

function pdfExport_map(site, siteData, req){
    phantom.pdf_export_map(site, siteData, req, pdfExport_census);
}

function pdfExport_census(site, siteData, req){
    phantom.pdf_export_census(site, siteData, req, pdfExport_ue);
}

function pdfExport_ue(site, siteData, req){
    phantom.pdf_export_ue(site, siteData, req, pdfExport_decay);
}

function pdfExport_decay(site, siteData, req){
    phantom.pdf_export_decay(site, siteData, req, processSites);
}

function processSites(req) {
        var pdfMerge = new PDFMerge(req.fileStack, 'C:\\Program Files (x86)\\PDFtk\\bin\\pdftk.exe');
        var d = Date.now();
        var filename = 'r' + d.toString() + '.pdf';
        pdfMerge.asNewFile(process.env.FILEPATH + filename).merge(function (error, file) {
            console.log(file);
        });
}

如果一个用户运行该进程,这非常有效。如果多个用户同时运行该进程,则只有一个用户获得报告。这个过程失败了幻影的承诺。当我在以下函数中设置断点时,我看到在将站点A导出为PDF(断点2)之前,已收到站点B的数据(断点1)。在此阶段,站点B的流程永远不会回升。

function pdf_export_map(site, siteData, req, _pdfExport_census) {
    phantom.create()
        .then(instance => {
            ph = instance;
            return instance.createPage();
        })
        .then(p => {
            page = p;
            page.open('about:blank');
        })
        .then(function () {
            var renderTimeout,
                c = 0;

            function doRender() {
                console.log('page loaded');
                var d = Date.now();
                var filename = 'm' + d.toString() + '.pdf';
                page.render('tmp/' + filename)
                    .then(function () {


//breakpoint 2 page PDF export complete
                        ph.exit();
                        req.fileStack.push(process.env.FILEPATH + filename);
                        _pdfExport_census(site, siteData, req)
                    });
            }

            page.on('onResourceRequested', function (r) {
                c++;
                console.log(('000' + r.id).slice(-4) + ' | ' + r.url);
                clearTimeout(renderTimeout);
            });

            page.on('onResourceReceived', function (r) {
                if (!r.stage || r.stage === 'end') {
                    c--;
                    console.log(('000' + r.id).slice(-4) + ' | ' + r.status);
                    if (c === 0) {
                        renderTimeout = setTimeout(doRender, 5000);
                    }
                }
            });

            if (process.platform == 'win32') {
                page.property('paperSize', {
                    width: '11in',
                    height: '8.5in'
                });
            } else {
                page.property('paperSize', {
                    width: '1056px',
                    height: '816px'
                });
            }

            page.property('viewportSize', {
                width: 1056,
                height: 816
            });

            page.property('clipRect', {
                top: 0,
                left: 0,
                width: 1056,
                height: 816
            });


//breakpoint 1 data received, start render
            ejs.renderFile('views/phantommap.ejs', {
                site: site,
                features: siteData.features,
                subdirectory: process.env.SUBDIRECTORY,
                page: req.fileStack.length + 1
            }, function (err, html) {
                page.setContent(html, 'http://' + process.env.HOST + '/amazon/phantommap');
            });

        })
        .catch(err => {
            console.log(err);
            ph.exit();
        });
}

我对幻影和承诺的理解很差。是否可以运行此同步,例如完成一个站点的页面,然后启动第二个报告的页面?

编辑:做了一些测试我评论了ph.exit()。我看到我收到了站点A和站点B的数据。但站点A的页面导出了两次。

编辑:根据Amir的推荐,我通过在函数中声明ph和页面来实现这一点。

phantom.create()
    .then(instance => {
        _ph = instance;
        return instance.createPage();
    })
    .then(p => {
        _page = p;
        _page.open('about:blank');
    })
    .then(function () {
        var renderTimeout,
            c = 0,
            ph = _ph,
            page = _page;

我仍然需要研究使用async和await。

1 个答案:

答案 0 :(得分:2)

我怀疑您的代码不是线程安全的。我看到你有一个名为ph的变量。这是一个跨多个请求共享的全局变量吗?如果是这样,那就有问题了。您正在共享实例。相反,您不应该使用任何全局变量并仅为该请求关闭幻像实例。

https://github.com/amir20/phantomjs-node/issues/583处有关于幽灵的类似问题。但问题在于它正在重复使用幻影过程的实例。

为您的代码提出几点建议。请改用asyncawait。这个回调地狱会让你发疯。对于每个请求,请勿使用在会话外声明的任何全局变量。在请求之间共享数据会产生意外结果。

免责声明:我是PhantomJs-Node的作者。