[英]NodeJS and Electron - request-promise in back-end freezes CSS animation in front-end
注意:附加信息作为编辑#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>
- JS -
form.addEventListener('submit', function(e) {
e.preventDefault();
loader.classList.remove('_hide');
setTimeout(function() {
ipcRenderer.send('credentials:submit', credentials);
}, 0)
});
其中._hide
很简单
._hide {
visibility: hidden;
}
并且ipcRenderer.send()
是异步方法 ,没有其他选项。
问题
通常 , 0ms
延迟足以允许在阻塞事件发生之前更改DOM。 但不是在这里。 无论是否使用setTimeout()
,仍然存在延迟。
所以,加上一点延迟......
loader.classList.remove('_hide');
setTimeout(function() {
ipcRenderer.send('credentials:submit', credentials);
}, 100);
大! 装载机在提交后立即显示! 但是...在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...
});
}
有缺陷的冻结行为仅在第一次API调用时发生 。 连续的API调用(即刷新桌面应用程序而不重新启动NodeJS后端)不会导致挂起。 即使我调用不同的API方法,也没有问题。
目前,我已经实施了以下hacky解决方法:
首先,使用show:false
初始化第一个BrowserWindow
...
window = new BrowserWindow({
show: false
});
窗口准备就绪后,向外部API发送ping,并仅在成功响应后显示窗口...
window.on('ready-to-show', function() {
apiWrapper.ping(function(response) {
if(response.error) {
app.quit();
}else {
window.show(true);
}
});
});
这个额外的步骤意味着在窗口出现之前有大约500ms的延迟,但随后所有连续的API调用(无论是.ping()
还是其他)都不再阻止UI。 我们正处于回调地狱的边缘,但这并不算太糟糕。
所以...这是一个request-promise
问题(根据我从文档中可以看出,这是异步的)。 不确定为什么这种行为只会在第一次通话时出现 ,所以如果您知道,请随时告诉我! 否则,这个小小的hacky就必须要做了。
(注意:我是唯一一个会使用这个桌面应用程序的人,因此我不太担心显示“ping失败”消息。对于商业版本,我会提醒用户API调用失败。)
值得检查请求承诺如何在内部设置模块加载。 阅读它,似乎在调用请求时有一种延迟加载( 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));
返回值如下:
{ seconds: 0.00421092,
milliseconds: 4.21092,
nanoseconds: 4210920 }
{ seconds: 0.000511664,
milliseconds: 0.511664,
nanoseconds: 511664 }
第一次通话显然比后续通话时间长。 (当然可能会有所不同,我在相对较快的cpu上运行裸节点.js)如果模块加载是第一次调用的主要成本,那么它将阻止主进程直到模块加载(导致node.js require
解析是同步的) )
我不能说这是具体原因,但值得检查。 正如评论中所建议的那样,尝试使用其他lib或裸内部模块(如Electron的net
)来排除。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.