在 JavaScript 中强制同步重绘是否可能?

4

我想强制进行同步重绘,这可行吗?

不幸的是,我正在使用的库通过XMLHttpRequest.prototype.open(_, _, false)发送长时间运行的同步请求。我试图通过猴子补丁XMLHttpRequest.prototype.send在请求发送之前更新页面并显示加载指示器,但是opacity: 0.7样式从未被看到:

const oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(...args) {
  document.body.style.opacity = "0.7";

  // is there something I can do to force a repaint on this line?

  const retVal = oldSend.bind(this)(...args);
  document.body.style.opacity = "1";
  return retVal;
}

我的大部分研究都建议重构为异步操作:

但是,我无法控制进行同步请求的代码。

编辑:另一个相关帖子:


1
只是提醒一下,oldSend.bind(this)(...args) 可以改为 oldSend.apply(this, args) - 4castle
既然你可以猴子补丁这个函数,为什么不改变你的DOM,然后在一个短的setTimeout上调用原始的慢同步函数呢?期待看到更好的做法 :-)。 - Joshua R.
1
不,这是不可能的。你可以强制重新排版,但不能重绘。顺便说一句,像这样的情况正是为什么SJAX已被弃用的原因。 - Bergi
2
@JoshuaR。有相同的想法,这对特定的send来说是可行的,因为它无论如何都返回undefined,但是响应将立即对调用者可用。 - Bergi
@Ryan 这是一些专有软件(不是我们的)。我无法访问任何源代码。 - uber5001
显示剩余3条评论
2个回答

2
可以在HTML规范中找到有关为什么无法在执行同步代码的中间更新屏幕的解释:
定义如下: 为了协调事件、用户交互、脚本、渲染、网络等,用户代理必须使用本节所述的事件循环。
这意味着与执行脚本的调用相关的渲染是从事件循环中分离出来的单独调用。
在处理模型下进一步详细说明如下: 选择一个任务并运行它[步骤1-6] 更新渲染:如果此事件循环是浏览上下文事件循环(而不是工作线程事件循环),则运行以下子步骤。 因此,在事件循环的单个调用中无法从浏览上下文中在执行同步代码的中间更新屏幕。
将同步代码移动到Web Worker可能是解决方案的基础(Worker在单独的线程中执行),但超出了此答案的范围。

我认为这不会阻止API允许从脚本手动调用这些步骤,但是它并没有被设计成需要一个。 - Bergi

1
根据评论、研究和我的经验,看起来这是不可能的。
对于我的用例,我将通过在页面上始终有动画效果的东西来使页面“冻结”加载指示器。或者更有可能的是,放弃加载指示器功能。

1
如果有人有确凿的证据表明同步重绘不可能,请在回答中发表,我会接受它。 - uber5001

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接