注意:附加信息作为编辑#1 附加到原始问题的末尾,详细说明了后端request-promise
如何导致UI冻结。请记住,纯CSS动画暂时挂起,你可能只是跳到编辑(或完整阅读全部)
设置
我使用Electron在桌面网络应用上工作。
有一次,用户需要输入并提交一些数据。当他们点击"提交"时,我使用JS来显示this css loading animation(右下角加载器),并将数据异步发送到后端......
- 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()
为async method,其他选项无法设置。
问题
通常,0ms
延迟足以允许在阻塞事件发生之前更改DOM。但不是在这里。无论是否使用setTimeout()
,仍有延迟。
所以,加上一点延迟......
loader.classList.remove('_hide');
setTimeout(function() {
ipcRenderer.send('credentials:submit', credentials);
}, 100);
大!装载机在提交后立即显示!但是......在100ms之后,动画在其轨道上停止了大约500ms左右,然后再回到chooching。
这项工作 - &gt;不工作 - &gt;无论延迟长度如何,都会发生工作模式。一旦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解决方法:
首先,使用BrowserWindow
...
show:false
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);
}
});
});
此额外步骤意味着在窗口出现之前有大约500毫秒的延迟,但随后所有连续的API调用(无论.ping()
还是其他)都不再阻止UI。我们已经到了callback hell的边缘,但这并不是太糟糕。
所以......这是一个request-promise
问题(据我所知,从文档中可以看出异步)。不确定为什么这种行为只会出现在 第一次调用 上,所以如果您知道,请随时告诉我们!否则,这个小小的hacky就必须要做了。
(注意:我是唯一一个会使用此桌面应用的人,因此我并不担心显示&#34; ping失败&#34;消息。商业版本,我会提醒用户API调用失败。)
答案 0 :(得分:2)
值得检查请求承诺如何在内部设置模块加载。阅读它,似乎在调用请求时有一种延迟加载(https://github.com/request/request-promise/blob/master/lib/rp.js#L10-L12)。快速试用
async function foo() {
var outCursor = await db.Working_newFooditem.find({}).toArray()
var inCursor = await db.Working_newFooditem.find({}).toArray()
for (var out_c of outCursor) {
var outUniqueString = out_c.uniqueName
for (var in_c of inCursor) {
var inUniqueString = in_c.uniqueName
if (in_c === out_c) {
await db.dupC.insert(in_c)
await db.Working_newFooditem.remove(in_c._id)
}
}
}
return 'good job'
}
foo()
.then(msg => console.log(msg))
.catch(e => console.log(e))
返回如下所示的值:
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));
第一次通话显然比后续通话时间长。 (当然可能会有所不同,我在相对较快的cpu上运行裸节点.js)如果模块加载是第一次调用的主要成本,那么它将阻止主进程直到加载模块(导致node.js {{1} } resolve is synchronous)
我不能说这是具体原因,但值得检查。正如评论中所建议的那样,尝试使用其他lib或裸内部模块(如Electron的{ seconds: 0.00421092,
milliseconds: 4.21092,
nanoseconds: 4210920 }
{ seconds: 0.000511664,
milliseconds: 0.511664,
nanoseconds: 511664 }
)来排除。