简体   繁体   English

如何解决阻塞的JavaScript进程?

[英]How can I fix blocking JavaScript processes?

General Questions 一般的问题

Hi, I tried to move logic to a process using setTimeout in order to show a loading screen right away and update the page once the process is complete. 嗨,我尝试使用setTimeout将逻辑移至流程,以便立即显示加载屏幕,并在流程完成后更新页面。

Sometimes it kind of works, sometimes it does not. 有时它会起作用,有时却不会。 Now I think I need to know some more theory to get to the bottom of this problem. 现在,我想我需要了解更多理论才能深入研究这个问题。

Is there always a programming error involved when a website blocks the whole browser (Firefox) or everything in a tab (Chrome)? 网站阻止整个浏览器(Firefox)或选项卡中的所有内容(Chrome)时,总会涉及编程错误吗?

Can it be a case of leaky abstraction (like a C/C++ game loop that never sleeps and does not allow the CPU to work on other processes)? 可能是抽象泄漏的情况(例如C / C ++游戏循环永不休眠并且不允许CPU在其他进程上工作)吗? Do I have to write a mechanism that works with several setTimeouts if the process takes long and has a lot of work to do? 如果过程花费很长时间并且有很多工作要做,我是否必须编写一种与多个setTimeouts一起使用的机制?

Anything else I need to look into / keep in mind? 我还有什么需要研究/记住的吗?

Details 细节

I have read https://softwareengineering.stackexchange.com/questions/202047/what-determines-which-javascript-functions-are-blocking-vs-non-blocking and it does not seem to answer the questions. 我已阅读https://softwareengineering.stackexchange.com/questions/202047/what-determines-which-javascript-functions-are-blocking-vs-non-blocking ,它似乎无法回答问题。

The problem can be seen here: http://procgames.com/raidaces/ 问题可以在这里看到: http : //procgames.com/raidaces/

Firefox sometimes is blocked and does not update anything until the whole process is complete (weird, because I render the loading state in the canvas before I call the loading process). Firefox有时会被阻止,并且在整个过程完成之前不会更新任何内容(这很奇怪,因为在调用加载过程之前,我在画布上渲染了加载状态)。

In Chrome the text is blocked (can not be selected) until the process is complete, so I assume the whole Tab is frozen. 在Chrome中,该文本将被阻止(无法选择),直到该过程完成为止,因此,我认为整个选项卡都已冻结。

What happens is: 25 images (1024x1024) are rendered in canvases and moved to img entities using toDataURL (so the client is pretty busy). 发生的是:在画布中渲染了25张图像(1024x1024),并使用toDataURL将其移动到img实体(因此客户端非常忙)。

Maybe I am doing something wrong? 也许我做错了什么? The program starts in the HTML file: 该程序在HTML文件中启动:

        <div class=container>
        <h1>Raid Aces</h1>

        <canvas id="gameCanvas" width="800" height="600" onClick="toggleRunning(gameContext)"></canvas>
        <div id="debugArea">The Epic Battles Of The Ancient Clans ... or whatever ...</div>
        <canvas id="mapCanvas" width="128" height="128"></canvas>

        <div class="clear"></div>
        <div id="hiddenContentArea"></div>

        <script>

        var gameCanvas = document.getElementById('gameCanvas');
        var gameContext = gameCanvas.getContext('2d');
        var hiddenContentArea = document.getElementById('hiddenContentArea');

        var mapCanvas = document.getElementById('mapCanvas');
        var mapContext = mapCanvas.getContext('2d');

        gameContext.beginPath();
        gameContext.rect(0, 0, 800, 600);
        gameContext.fillStyle = "#88BB88";
        gameContext.fill();
        gameContext.font = "bold 16px sans-serif";
        gameContext.fillStyle = "#FFFFFF";
        gameContext.fillText("Loading", 350, 294);

        mapContext.beginPath();
        mapContext.rect(0, 0, 128, 128);
        mapContext.fillStyle = "#88BB88";
        mapContext.fill();
        mapContext.font = "bold 10px sans-serif";
        mapContext.fillStyle = "#FFFFFF";
        mapContext.fillText("Loading", 42, 62);

        initEngine(gameContext);
        initGame(gameContext, hiddenContentArea);

        document.addEventListener('keydown', function(event) {
            handleInputEvent(event, gameContext);
        });
        </script>
    </div>

which calls initGame ... 哪个调用了initGame ...

function initGame(gameContext, hiddenContentArea) {

    if (!engine.isInitialized) {
        throw "error_engine_not_initialized";
    }

    var eventsModule = engine.getModule("events");
    log("Rendering Map, please be patient!");
    eventsModule.changeGameState(new GameStateLoading(engine));
}

... and this is where the asynchronous call should be realized: ...,这是应该实现异步调用的地方:

ModuleEvents.prototype.changeGameState = function(moduleGameState) {

    var oldGameState = this.gameState;
    if (oldGameState) {
        oldGameState.uninit();
    }

    this.gameState = moduleGameState;
    var callbackFunction = this.makeCallbackExecution(oldGameState,
            this.gameState, this.eventBus);

    setTimeout(callbackFunction, 10);
};

ModuleEvents.prototype.makeCallbackExecution = function(oldGameState,
        newGameState, eventBus) {

    return function() {
        newGameState.init();
        eventBus
                .fireEvent(new EventGameStateChanged(oldGameState, newGameState));
    };
};

I've looked at your project using Chrome Developer Tools (CDT) Timeline option and noticed the following: 我已经使用Chrome开发者工具(CDT)时间线选项查看了您的项目,并注意到以下内容:

CDT时间轴视图

I'm not a CDT expert but it seems like these 25 timers are set simultaneously (in a loop at gamestate_loading.js:109) and are going to fire at the same time. 我不是CDT专家,但似乎这25个计时器是同时设置的(在gamestate_loading.js:109的循环中),并且将同时触发。 Now imagine you're a thread serving a web page. 现在,假设您是提供网页的线程。 Your duties is to draw UI (maybe in response to user) and execute JS. 您的职责是绘制UI(也许响应用户)并执行JS。 But suddenly here comes 25 JS tasks as well as usual GUI requests. 但是突然出现了25个JS任务以及通常的GUI请求。 I don't know details of timers scheduling, but I don't think that firing plenty of them at the same time is a good idea. 我不知道计时器调度的详细信息,但我不认为同时触发大量计时器是一个好主意。

And yes, these 25 timers will be executed serially, one after another because of javascript's single thread nature. 是的,由于javascript的单线程特性,这25个计时器将依次执行,一个接一个。

I'd recommend you to investigate applicability of WebWorkers to your case. 我建议您调查WebWorkers在您的案例中的适用性。 They're designed in order to separate heavy computations from UI. 它们旨在将大量计算与UI分开。 Also you may want to maximally optimize work you're doing in timers' callbacks (minimize DOM and rendering operations, for example). 另外,您可能希望最大程度地优化您在计时器的回调中所做的工作(例如,最小化DOM和渲染操作)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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