为什么JS为什么要等到功能完成才可以更改背景色? [英] Why does JS wait until function is complete before changing background color?

查看:50
本文介绍了为什么JS为什么要等到功能完成才可以更改背景色?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么在我复印时背景没有正确改变?我添加了 console.log(),并且您可以看到, console.log()可以工作,但是背景不会改变.这是什么问题?

Why doesn't the background change right as I copy? I added a console.log() and as you can see, the console.log() works, but the background won't change. What is the problem here?

要进行测试,请点击代码段,然后按 CMD + C (Windows: CTRL + C )

To test, click on the snippet and then press CMD + C (Windows:CTRL + C)

window.addEventListener('copy', function(e) {
  e.preventDefault();

  //This should change!
  document.getElementById("object").style.backgroundColor = 'white';
  console.log("Started!");

  tryCopyAsync(e).then(() =>
    document.getElementById("object").style.backgroundColor = 'gray'
  );
});
async function tryCopyAsync(e){
  if(navigator.clipboard){
    await e.clipboardData.setData('text/plain',getText());
  }
}
function getText(){
  var html = '';
  var row = '<div></div>';
  for (i=0; i<100000; i++) {
      html += row;
  }
  return html;
}

#object{
  width:100%;
  height:100vh;
  background:gray;
}
body{
  padding:0;
  margin:0;
  overflow:hidden;
}

<div id='object'></div>

推荐答案

首先,即使调用它,您的 sleepFor 方法也将完全阻塞事件循环 来自 async 函数:

First, your sleepFor method is completely blocking the event-loop synchronously, even if it is called from an async function:

window.addEventListener('copy', function(e) {
  e.preventDefault();

  //This should change!
  document.getElementById("object").style.backgroundColor = 'white';
  console.log("Started!");

  tryCopyAsync(e).then(() =>
    document.getElementById("object").style.backgroundColor = 'gray'
  );
  console.log('sync');
});
async function tryCopyAsync(e){
  if(navigator.clipboard){
    await e.clipboardData.setData('text/plain',getText());
  }
}
function sleepFor(sleepDuration){
  var now = new Date().getTime();
  while(new Date().getTime() < now + sleepDuration){} 
}
function getText(){
  console.log('blocking');
  var html = '';
  var row = '<div></div>';
  for (i=0; i<10; i++) {
      html += row;
      sleepFor(300);
  }
  console.log('stopped blocking');
  return html;
}
onclick = e => document.execCommand('copy');

#object{
  width:100%;
  height:100vh;
  background:gray;
}
body{
  padding:0;
  margin:0;
  overflow:hidden;
}

click to trigger the function
<div id='object'></div>

但是即使在微任务中调用它,也不会改变任何事情,因为

But even if it were called in a microtask, that wouldn't change a thing, because microtasks also do block the event-loop. (Read that linked answer, it explains how the rendering is tied to the event-loop).

如果要让代码让浏览器进行重绘,则需要让浏览器实际循环事件循环,并且唯一的方法是:

If what you want is to have your code let the browser do its repaints, you need to let the browser actually loop the event-loop, and the only ways to do this are:

  • split your getText logic and make it wait for the next event-loop iteration by posting a task (e.g through setTimeout)
  • use a dedicated Worker to produce the data returned by getText.

但是请注意,您不是在使用异步剪贴板API,而只是覆盖了复制事件的默认值,该值不能异步完成.因此,按照这种方式,您实际上将需要真正使用剪贴板API.

However beware you were not using the async Clipboard API, but simply overriding the default value of the copy event, which can not be done asynchronously. So going this way you will actually need to really use the Clipboard API.

以下是使用 MessageChannel 进行发布的示例任务,因为当前稳定的Chrome仍然对 setTimeout

Here is an example using a MessageChannel to post a task since current stable Chrome still has a 1ms minimum delay for setTimeout:

window.addEventListener('copy', function(e) {
  e.preventDefault();

  //This should change!
  document.getElementById("object").style.backgroundColor = 'white';
  console.log("Started!");

  tryCopyAsync(e).then(() =>
    document.getElementById("object").style.backgroundColor = 'gray'
  );
});
async function tryCopyAsync(e) {
  if (navigator.clipboard) { // you were not using the Clipboard API here
    navigator.clipboard.writeText(await getText());
  }
}
async function getText() {
  var html = '';
  var row = '<div></div>';
  for (i = 0; i < 1000000; i++) {
    if (i % 1000 === 0) { // proceed by batches of 1000
      await waitNextFrame();
    }
    html += row;
  }
  return html;
}

function waitNextFrame() {
  return new Promise(postTask);
}

function postTask(task) {
  const channel = postTask.channel ||= new MessageChannel();
  channel.port1.addEventListener("message", () => task(), {
    once: true
  });
  channel.port2.postMessage("");
  channel.port1.start();
}
onclick = (evt) => document.execCommand("copy");

#object {
  width: 100%;
  height: 100vh;
  background: gray;
}

body {
  padding: 0;
  margin: 0;
  overflow: hidden;
}

<div id='object'></div>

这篇关于为什么JS为什么要等到功能完成才可以更改背景色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆