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解决方法:
首先,BrowserWindow用show: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)来排除。