NodeJS和Electron - 后端的请求承诺冻结了前端的CSS动画

Bir*_*rel 7 html javascript css node.js electron

注意:附加信息作为编辑#1附加到原始问题的末尾,详细说明request-promise后端如何导致UI冻结.请记住,纯CSS动画暂时挂起,你可能只是跳到编辑(或完全阅读全部)

设置

我正在使用Electron开发桌面webapp .

有一次,用户需要输入并提交一些数据.当他们点击"提交"时,我使用JS来显示这个css加载动画(右下角加载器),并将数据异步发送到后端...

- HTML -

<button id="submitBtn" type="submit" disabled="true">Go!</button>

<div class="submit-loader">
    <div class="loader _hide"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

- JS -

form.addEventListener('submit', function(e) {
    e.preventDefault();

    loader.classList.remove('_hide');

    setTimeout(function() {
        ipcRenderer.send('credentials:submit', credentials);
    }, 0)
});
Run Code Online (Sandbox Code Playgroud)

在哪里._hide简单

._hide {
    visibility: hidden;
}
Run Code Online (Sandbox Code Playgroud)

并且其中ipcRenderer.send()是一个异步方法,而不选项的设置,否则.

问题

通常,0ms延迟足以允许在阻塞事件发生之前更改DOM.但不是在这里.无论是否使用setTimeout(),仍有延迟.

所以,加上一点延迟......

loader.classList.remove('_hide');

setTimeout(function() {
    ipcRenderer.send('credentials:submit', credentials);
}, 100);
Run Code Online (Sandbox Code Playgroud)

大!装载机在提交后立即显示!但是...在100ms之后,动画在其轨道上停止了大约500ms左右,然后又回到了chooching.

无论延迟长度如何,这种工作 - >不工作 - >工作模式都会发生.一旦ipcRenderer开始做东西,一切都停止了.

所以为什么!?

这是我第一次看到这种行为.我非常精通HTML/CSS/JS,但我不熟悉NodeJS和Electron.为什么我的纯CSS动画被停止了ipcRenderer,我该怎么做才能解决这个问题呢?

编辑#1 - 附加信息

在后端(NodeJS)中,我使用request-promise来调用外部API.当后端收到ipcRenderer消息时会发生这种情况.

var rp = require('request-promise');

ipcMain.on('credentials:submit', function(e, credentials) {    

    var options = {
        headers : {
            ... api-key...
        },
        json: true,
        url : url,
        method : 'GET'
    };

    return rp(options).then(function(data) {
        ... send response to callback...
    }).catch(function(err) {
        ... send error to callback...
    });

}
Run Code Online (Sandbox Code Playgroud)

有缺陷的冻结行为仅在第一次API调用时发生.连续的API调用(即刷新桌面应用程序而不重新启动NodeJS后端)不会导致挂起.即使我调用不同的API方法,也没有问题.

目前,我已经实施了以下hacky解决方法:

首先,BrowserWindowshow:false... 初始化第一个

window = new BrowserWindow({
    show: false
});
Run Code Online (Sandbox Code Playgroud)

窗口准备就绪后,向外部API发送ping,并仅在成功响应后显示窗口...

window.on('ready-to-show', function() {
    apiWrapper.ping(function(response) {
        if(response.error) {
            app.quit();
        }else {
            window.show(true);
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

这个额外步骤意味着在窗口出现之前有大约500ms的延迟,但随后所有连续的API调用(无论是否.ping())都不再阻止UI.我们正处于回调地狱的边缘,但这并不算太糟糕.

所以......这是一个request-promise问题(就我从文档中可以看出来而言,这是异步的).不确定为什么这种行为只出现在第一次通话中,所以如果你知道的话请随时告诉我!否则,这个小小的hacky就必须要做了.

(注意:我是唯一一个会使用这个桌面应用程序的人,因此我不太担心显示"ping失败"消息.对于商业版本,我会提醒用户API调用失败.)

小智 2

值得检查 request-promise 内部如何设置模块加载。阅读它,调用请求时似乎存在某种延迟加载(https://github.com/request/request-promise/blob/master/lib/rp.js#L10-L12 )。快速试用

const convertHrtime = require('convert-hrtime');

const a = require('request-promise');

const start = process.hrtime();
a({uri: 'https://requestb.in/17on4me1'});
const end = process.hrtime(start);
console.log(convertHrtime(end));

const start2 = process.hrtime();
a({uri: 'https://requestb.in/17on4me1'});
const end2 = process.hrtime(start2);
console.log(convertHrtime(end2));
Run Code Online (Sandbox Code Playgroud)

返回值如下:

{ seconds: 0.00421092,
  milliseconds: 4.21092,
  nanoseconds: 4210920 }
{ seconds: 0.000511664,
  milliseconds: 0.511664,
  nanoseconds: 511664 }
Run Code Online (Sandbox Code Playgroud)

第一次通话显然比后续通话花费的时间更长。(当然数量可能会有所不同,我在相对较快的CPU上在裸露的node.js上运行了这个)如果模块加载是第一次调用的主要成本,那么它将阻塞主进程,直到加载模块(因为node.js解析是require同步的)

我不能说这是具体原因,但值得检查。正如评论中所建议的,尝试其他库或裸内部模块(如 Electron 的net)来排除。