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

Posted

技术标签:

【中文标题】NodeJS 和 Electron - 后端的请求承诺冻结前端的 CSS 动画【英文标题】:NodeJS and Electron - request-promise in back-end freezes CSS animation in front-end 【发布时间】:2018-07-03 23:25:56 【问题描述】:

注意:附加到原始问题末尾的附加信息为 Edit #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);

太棒了!加载程序在提交后立即显示!但是... 100 毫秒后,动画停止在其轨道上,大约 500 毫秒左右,然后重新开始选择。

无论延迟长度如何,这种工作 -> 不工作 -> 工作模式都会发生。只要ipcRenderer 开始做事,一切都会停止。

那么...为什么!?

这是我第一次看到这种行为。我非常精通 HTML/CSS/JS,但对 NodeJS 和 Electron 来说我是公认的新手。为什么我的 pure 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);
        
    );
);

这个额外的步骤意味着在窗口出现之前有大约 500 毫秒的延迟,但随后所有连续的 API 调用(无论是.ping() 还是其他)都不再阻塞 UI。我们正接近callback hell,但这还不错。

所以...这是一个request-promise 问题(据我从文档中得知,这是异步)。不知道为什么这种行为只出现在第一次调用,所以如果你知道,请随时告诉我!否则,现在就不得不做一些小技巧了。

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

【问题讨论】:

我遇到了类似的问题。这在我的情况下修复了它github.com/electron/electron/issues/1854#issuecomment-167606793 @Vector 我不确定我是否看到了它。众所周知,代码正是我处理它的方式(使用ipcRendereripcMain)。我已经对原始问题添加了一个编辑,因为似乎问题出在request-proimise 你可以试试axios等其他模块 【参考方案1】:

值得检查 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));

返回值如下:

 seconds: 0.00421092,
  milliseconds: 4.21092,
  nanoseconds: 4210920 
 seconds: 0.000511664,
  milliseconds: 0.511664,
  nanoseconds: 511664 

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

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

【讨论】:

感谢您花时间梳理代码并进行测试。如果没有任何配置选项,我认为我的 hacky 解决方法将是我最好的选择。我会查看其他库,但是将我拥有的所有 API 包装器转换为它并不是一个微不足道的时间。下一次,在我走得太远之前,我肯定会做一些单元测试。

以上是关于NodeJS 和 Electron - 后端的请求承诺冻结前端的 CSS 动画的主要内容,如果未能解决你的问题,请参考以下文章

electron打包后http请求为啥不存在跨域了

处理从 React 应用程序到 NodeJS 后端的长时间运行的发布请求时的建议

[electron 工具] 使用 electronvue 和 nodejs 做一个 SOAP 测试工具之一 ( 简介 )

Windows Electron初探

nodejs/electron-forge/node-gyp:在尝试了我可以在网上找到的所有提示后,找不到任何要使用的 Visual Studio 安装

nodejs概述