【问题标题】:How to wait for a function, which calls itself recursively with setTimeout, to complete?如何等待使用 setTimeout 递归调用自身的函数完成?
【发布时间】:2021-03-21 13:17:14
【问题描述】:

我是 Javascript 新手,目前正在开发一个网站,随着时间的推移它会自行改变外观。

此页面的一部分是“打字机”,它一个字母一个字母地写出一个文本。 这是这台打字机的代码:

function typeWriter(element, txt) {
    if (txt.length > 1) {
        element.textContent += txt.charAt(0);
        var newText = txt.slice(1,txt.length);
        setTimeout(typeWriter, 150 , element, newText);
    } else {
        element.textContent += txt.charAt(0);
    }
}

现在我想等待打字机功能完成其文本,然后再进行另一项更改以假设我的背景颜色。

function runThis(){
    var line1 = document.getElementById("line1");
    typeWriter(line1, "This should be written first, before continuing");
    document.body.style.backgroundColor = "blue";
}

据我了解,setTimeout 使我的打字机异步,所以如果我按照上面的示例执行此操作,则只要打字机达到第一个 setTimeout,第三行代码就会运行。

我试图通过 async/await 条款和承诺来实现这一点。但即使在我解决了承诺之后,我的“runThis”功能在打字机完成后也不会继续。

function typeWriter(element, txt) {
    return new Promise (function(resolve,reject) {
        if (txt.length > 1) {
            element.textContent += txt.charAt(0);
            var newText = txt.slice(1,txt.length);
            setTimeout(typeWriter, 150, element, newText);
        } else {
            element.textContent += txt.charAt(0);
            resolve();
        }
    })
}


async function runThis() {
    var line1 = document.getElementById("line1");
    await typeWriter(line1, "papupa");
    console.log("waiting over")
    document.body.style.backgroundColor = "blue";
}

你能帮我弄清楚这里出了什么问题吗? 非常感谢

【问题讨论】:

  • 这是因为第一次调用 typeWriter 返回的承诺永远不会被解决,你只是在解决最后一个被创建的承诺。
  • 感谢您解释我哪里出错了!

标签: javascript asynchronous animation recursion


【解决方案1】:

您可以将setTimeout 包装在一个promise 中。这将允许您使用async/await 语法更清楚地表达代码的意图,几乎就像它同步运行一样。


async function runThis() {
  var line1 = document.getElementById("line1");
  await typeWriter(line1, "papupa");
  document.body.style.backgroundColor = "blue";
}

async function typeWriter(element, txt) {
  for (var i = 0; i < txt.length; i++) {
    element.textContent += txt[i]; // this is shorthand for txt.charAt(i)
    await pause();
  }
}

function pause() {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve, 150);
  });
}

【讨论】:

  • 我想我最喜欢这个解决方案。这真的很容易理解。使用它时,在最佳实践方面我应该注意什么?
【解决方案2】:

您创建了多个 Promise,并且只解决了最后一个 Promise。 您可以将递归函数定义为内部函数,这是一个示例:

function typeWriter(element, text) {
    return new Promise (function(resolve,reject) {
        function recursion(txt) {
            element.textContent += txt.charAt(0);
            if (txt.length > 1) {
                var newText = txt.slice(1, txt.length);
                setTimeout(recursion, 150, newText);
            } else {
                resolve();
            }
        }
        recursion(text);
    });
}

【讨论】:

  • 我也可以返回“resolve();”吗?在其他部分并将“return new Promise”部分移动到我的“runThis()”函数中?也许 const typeWriterPromise = new Promise (function(resolve, reject) { typeWriter(element, text);}?然后承诺会从打字机中解决?
  • @PIcreamsoda 我不确定我是否正确理解您的要求,但递归函数需要访问resolve。这就是为什么我在resolve 范围内的promise 中定义了它。
【解决方案3】:

您可以将函数作为另一个参数传递到最后运行。

function typeWriter(element, txt, callback) {
    if (txt.length > 1) {
        element.textContent += txt.charAt(0);
        var newText = txt.slice(1,txt.length);
        setTimeout(typeWriter, 150 , element, newText, cb);
    } else {
        element.textContent += txt.charAt(0);
        callback();
    }
}

typeWriter(el, "ABCDEF", runthis);

【讨论】:

  • 因此,如果我编写多行并在其间进行各种更改,则每次运行 typeWriter 时都需要不同的“runthis”,对吧?
  • 是的。您可以拥有一个带参数的函数,并使用匿名函数使用特定参数调用它,typeWriter(el, "ACBDEF", () =&gt; runthis("x"));
猜你喜欢
  • 2017-06-22
  • 2023-04-08
  • 1970-01-01
  • 2018-09-23
  • 2021-06-26
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 2012-02-07
相关资源
最近更新 更多