gpt4 book ai didi

javascript - 检查 JS 中的异步函数重入

转载 作者:搜寻专家 更新时间:2023-11-01 04:34:27 26 4
gpt4 key购买 nike

场景:

我们有一个 MutationObserver 处理函数 handler

handler 中,我们进行一些 DOM 操作,这会再次触发 handler。从概念上讲,我们会有一个可重入的 handler 调用。除了 MutationObserver 不在线程内运行,它会在 handler 已经完成执行后触发。

因此,handler 将触发自身,但通过异步队列,而不是线程内。 JS 调试器似乎知道这一点,它将自己作为调用堆栈中的异步祖先(即使用 Chrome)。

为了实现一些有效的事件去抖动,我们需要检测相同的;也就是说,如果 handler 由于自身触发的更改而被调用。

那怎么办呢?

mutationObserver=new MutationObserver(handler);
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});

var isHandling;
function handler(){
console.log('handler');

// The test below won't work, as the re-entrant call
// is placed out-of-sync, after isHandling has been reset
if(isHandling){
console.log('Re-entry!');
// Throttle/debounce and completely different handling logic
return;
}

isHandling=true;

// Trigger a MutationObserver change
setTimeout(function(){
// The below condition should not be here, I added it just to not clog the
// console by avoiding first-level recursion: if we always set class=bar,
// handler will trigger itself right here indefinitely. But this can be
// avoided by disabling the MutationObserver while handling.
if(document.getElementById('foo').getAttribute('class')!='bar'){
document.getElementById('foo').setAttribute('class','bar');
}
},0);

isHandling=false;
}


// NOTE: THE CODE BELOW IS IN THE OBSERVED CONTENT, I CANNOT CHANGE THE CODE BELOW DIRECTLY, THAT'S WHY I USE THE OBSERVER IN THE FIRST PLACE

// Trigger a MutationObserver change
setTimeout(function(){
document.getElementById('asd').setAttribute('class','something');
},0);

document.getElementById('foo').addEventListener('webkitTransitionEnd',animend);
document.getElementById('foo').addEventListener('mozTransitionEnd',animend);


function animend(){
console.log('animend');
this.setAttribute('class','bar-final');
}
#foo {
width:0px;
background:red;
transition: all 1s;
height:20px;
}
#foo.bar {
width:100px;
transition: width 1s;
}
#foo.bar-final {
width:200px;
background:green;
transition:none;
}
<div id="foo" ontransitionend="animend"></div>
<div id="asd"></div>

注意我们的用例在这里包含 2 个组件;一个我们称之为contents,它是任何普通的网络应用程序,有很多 UI 组件和界面。还有一个overlay,它是观察content变化并可能自己做变化的组件。

一个简单的想法是不够的,就是在处理时禁用MutationObserver;或者,假设对 handler 的每一次调用都是递归的;这在上面说明的 animationend 事件的情况下不起作用:contents 可以有处理程序,这些处理程序又可以触发异步操作。两个最常见的此类问题是:onanimationend/oneventendonscroll

因此,仅检测直接(首次调用)递归的想法是不够的,我们实际上需要调试器中调用堆栈 View 的等价物:一种判断调用是否(无论之后有多少异步调用)的方法) 是其自身的后代。

因此,这个问题不仅限于 MutationObserver,因为它必然涉及一种通用方法来检测调用树中它们自身的后代异步调用。实际上,您可以用任何异步事件替换 MutationObserver

上面例子的解释:在这个例子中,当#foo不是.bar。但是,contents 有一个 transitionend 处理程序,它将 #foo 设置为 .bar-final,这会触发一个恶意的 self -递归链。我们想放弃对 #foo.bar-final 变化的 react ,通过检测它是我们自己行动的结果(用 #foo.bar 开始动画) .

最佳答案

一个可能的解决方法是在触发一个突变时停止突变观察器

mutationObserver=new MutationObserver(handler);
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});

// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');

function handler(){
console.log('Modification happend')

mutationObserver.disconnect();
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');

mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
}

查看 JS fiddle

https://jsfiddle.net/tarunlalwani/8kf6t2oh/2/

关于javascript - 检查 JS 中的异步函数重入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58118551/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com