javascript - 如何减慢 JavaScript 代码的执行速度?

标签 javascript youtube

我正在编写一个脚本来删除我所有的 YouTube 评论。这段代码的每一行都可以删除一个注释,但是当我将其放入循环中时,我收到一个 Uncaught TypeError: Cannot read property 'click' of undefined ,当我运行时,它不会出现每行单独。我在想,如果我能找到一种在代码行之间休眠的方法,我就可以消除错误。

var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
for(i = 0; i < myList.length; i++) {
    myList[i].click();
    document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click(); //error here
    document.getElementById("confirm-button").click();
}

我尝试使用 setTimeout,如下所示:

var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
for(i=0; i<myList.length; i++) {
    myList[i].click();
    setTimeout(function(){document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click();}, 1000);
    setTimeout(function(){document.getElementById("confirm-button").click();}, 5000);
}

它返回了数字279,但没有错误,也没有删除评论。发生了什么?

最佳答案

以下所有选项都有点“hackish” - 但没有一致且简单的方法可以在点击事件完全处理后获得通知。 您可以将自己的单击事件监听器添加到目标元素,但是随后可以通过 stopPropagation 将其“静音”... 即使这样,也没有(简单)的方法可以知道 DOM 何时完成“重绘”作为点击事件的结果 - 你可以看看使用 MutationObserver ,我猜

我假设(永远不要这样做) document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4] 所针对的元素通过单击 myList[i].click(); 动态添加 这就是你遇到这个问题的原因。之后myList[i].click(); DOM 在“一段时间内”不会被“更新” - 所以,document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")很容易失败。

A potential issue is if deleting comments changes the number of elements targeted by document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer"); - as HTMLCollections are "live", removing a DOM element will mutate the HTMLCollection - only the last two code snippets will be immune to this possibility

因此,您可以通过四种不同的方式来实现此目的

<小时/>

选项 1 - ES5,只是简单的回调

var DELAY = 0; // try 0, then try increasing values
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
function confirmClick(callback3) {
    document.getElementById("confirm-button").click();
    setTimeout(callback3, DELAY);
}
function itemClick(callback2, callback3)  {
    document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click();
    setTimeout(callback2, DELAY, callback3);
}
function listClick(element, callback1, callback2, callback3) {
    element.click();
    setTimeout(callback1, DELAY, callback2, callback3);
}
function doOne(i) {
    listClick(myList[i], itemClick, confirmClick, function() {
        ++i;
        if (i < myList.length) {
            doOne(i);
        }
    });
}
doOne(0);
<小时/>

As you are trying to "chain" multiple asynchronous (sort of) processes together, Promise's (seem) to make the code a little less cumbersome

选项 2 - Promise,但带有嵌套 setTimeout的- 使用Array#reduce将点击链接在一起,以便它们严格地一个接一个地处理

这太丑了,真的很丑,让金字塔建筑留给死去的埃及人, 包括来说明我想要实现的目标

var DELAY = 0; // try 0, then try increasing values
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
var p = Promise.resolve();
for(i = 0; i < myList.length; i++) {
    myList[i].click();
    p = p.then(() => new Promise(resolve) => {
        setTimeout(() => {
            document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click();
            setTimeout(() => {
                document.getElementById("confirm-button").click();
                setTimeout(resolve, DELAY);
            }, DELAY);
        }, DELAY);
    });
}
<小时/>

选项 3 - Promise 带有辅助函数 - 基本上是上面的代码,但添加了辅助函数来防止金字塔

const DELAY = 0; // try 0, then try increasing values
const clickThenDelay = element => new Promise(resolve => {
    element.click();
    setTimeout(resolve, DELAY);
});
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");

Array.from(myList).reduce((p, item) => { 
    return p
    .then(() => clickThenDelay(item))
    .then(() => clickThenDelay(document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4]))
    .then(() => clickThenDelay(document.getElementById("confirm-button")))
}, Promise.resolve());
<小时/>

选项 4 - 与选项 3 类似,但此选项在触发单击之前添加了一个单击事件处理程序,该处理程序在“处理”单击后被删除。 大概最后添加的处理程序是最后调用的。但不确定这是否得到保证。 此外,如果较早的处理程序调用event.stop[Immediate]Propagation(),这将不起作用。

const DELAY = 0; // try 0, then try increasing values
const clickThenDelay = element => new Promise(resolve => {
    const handleClick = () => {
        element.removeEventListener('click', handleClick);
        setTimeout(resolve, DELAY); // wait for repaint? Perhaps a MutationObserver event could be useful here?
    }
    element.addEventListener('click', handleClick);
    element.click();
});
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");

Array.from(myList).reduce((p, item) => {
    return p
    .then(clickThenDelay(item))
    .then(() => clickThenDelay(document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4]))
    .then(() => clickThenDelay(document.getElementById("confirm-button")))
}, Promise.resolve());

关于javascript - 如何减慢 JavaScript 代码的执行速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49164262/

相关文章:

ios - 使用 API 3.0 获取推荐视频

youtube - 当我插入iframe youtube视频不起作用时

javascript - highcharts 响应式数据标签显示

javascript - YouTube 视频结束时隐藏 iframe

javascript - Google建议搜索多个单词

javascript - 如何测量 react 虚拟化列表中的行高

file - YouTube获取视频文件链接

python - 与Celery一起分发时,Youtube API OAuth的对象失去身份验证

javascript - Chrome 扩展程序 : Automatically download a screenshot taken with 'chrome.tabs.captureVisibleTab'

javascript - 将 Sweet alert 与 Typescript 类一起使用