1. 主页
  2. 文档
  3. 学习君土脚本
  4. 异步君土脚本
  5. 合作异步君土脚本:超时和间隔

合作异步君土脚本:超时和间隔

在这里,我们将讨论传统的异步君土脚本方法,这些方法可以在一段时间或一段规则间隔(例如,设定的每秒次数)之后,以异步方式运行代码,并讨论它们的用处,以及它们的固有问题。

介绍

很长一段时间以来,网页平台为君土脚本程序员提供了许多函数,这些函数允许您在一段时间间隔过后异步执行代码,或者重复异步执行代码块,直到您告诉它停止为止。

这些函数是:
设置超时/*setTimeout*/()
在指定的时间后执行一段代码.

设置间隔/*setInterval*/()
以固定的时间间隔,重复运行一段代码.

这些函数设置的异步代码实际上是在主线程上运行(在其指定的计时器过去之后)。

在 设置超时() 调用执行之前或 设置间隔() 迭代之间可以(并且经常会)运行其他代码。根据这些操作的处理器密集程度,它们可以进一步延迟异步代码,因为任何异步代码仅在主线程可用后才执行(换句话说,当调用栈为空时)。在阅读本文时,您将学到更多关于此问题的信息。

无论如何,这些函数用于在网站或应用程序上运行不间断的动画和其他后台处理。在下面的部分中,我们将向您展示如何使用它们。

设置超时()

正如前述, 设置超时() 在指定的时间后执行一段特定代码. 它需要如下参数:

  • 要运行的函数,或者函数引用。
  • 表示在执行代码之前等待的时间间隔(以毫秒为单位,所以1000等于1秒)的数字。如果指定值为0(或完全省略该值),函数将尽快运行(参阅下面的注释,了解为什么它“尽快”而不是“立即”运行)。稍后详述这样做的原因。
  • 更多的参数:在指定函数运行时,希望传递给函数的值。

注意: 指定的时间(或延迟)不能保证在指定的确切时间之后执行,而是最短的延迟执行时间。在主线程上的堆栈为空之前,传递给这些函数的回调将无法运行。

结果,像 设置超时(函数, 0) 这样的代码将在堆栈为空时立即执行,而不是立即执行。如果执行类似 设置超时(函数, 0) 之类的代码,之后立即运行从 1 到 100亿 的循环之后,回调将在几秒后执行。 

在下面的示例中,脚本运行环境将在执行匿名函数之前等待两秒钟,然后显示在控制台显示消息:

定 问候 = 设置超时(务() {
  控制台.日志('你好, 星辰大海!');
}, 2000);

我们指定的函数不必是匿名的。我们可以给函数一个名称,甚至可以在其他地方定义它,并将函数引用传递给 设置超时() 。以下两个版本的代码片段相当于第一个版本:

// 使用有名字的函数
定 问候0 = 设置超时(务 打招呼() {
  控制台.日志('你好, 星辰大海!');
}, 2000)

// 使用另外定义的函数
务 打招呼() {
  控制台.日志('你好, 星辰大海!');
}

定 问候1 = 设置超时(打招呼, 2000);

例如,如果我们有一个函数既需要从超时调用,也需要响应某个事件,那么这将非常有用。此外它也可以帮助保持代码整洁,特别是当超时回调超过几行代码时。

设置超时() 返回一个标志符变量用来引用这个间隔,可以稍后用来取消这个超时任务,下面就会学到 清除超时() 。

传递参数给 设置超时() 

我们希望传递给设置超时()中运行的函数的任何参数,都必须作为列表末尾的附加参数传递给它。

例如,我们可以重构之前的函数,这样无论传递给它的人的名字是什么,它都会向它打招呼:

务 打招呼(谁: 文) {
  控制台.日志(`你好, ${谁}!`);
}

人名可以通过第三个参数传进 设置超时() :

定 问候1 = 设置超时(打招呼, 2000, '星辰大海');

清除超时

最后,如果创建了 超时,您可以通过调用清除超时/*clearTimeout*/(),将设置超时()调用时返回的标识符作为参数传递给它,从而在超时运行之前取消。要取消上面的超时,你需要这样做:

清除超时(问候1);

设置间隔

当我们需要在一段时间之后运行一次代码时,设置超时()可以很好地工作。但是当我们需要反复运行代码时会发生什么呢,例如在动画的情况下?

这就是设置间隔()的作用所在。它与设置超时()的工作方式非常相似,只是作为第一个参数传递给它的函数将重复执行,而不是一次执行重复执行的时间间隔不少于第二个参数给出的毫秒数。您还可以将正在执行的函数所需的任何参数作为 设置间隔() 调用的后续参数传递。

让我们看一个例子。下面的函数创建一个新的日期/*Date*/()对象,使用转本地字串/*toLocaleTimeString*/()从中提取一个时间字符串,然后在控制台中显示它。然后,我们使用设置间隔()每秒运行该函数一次,创建一个每秒更新一次的数字时钟的效果。

  定 显示次数 = 0;
  务 显示时间() {
    定 日期0 = 启 日期();
    定 时间 = 日期0.转本地时间字串();
    控制台.日志(时间);

    显示次数 ++;
    若(显示次数 > 10) {
      清除间隔(创建钟表);
    }
  }

  常 创建钟表 = 设置间隔(显示时间, 1000);

设置超时()一样, 设置间隔() 返回一个确定的值,稍后你可以用它来取消间隔任务。

清除间隔

设置间隔()永远保持运行任务,除非我们做点什么——我们可能会想阻止这样的任务,否则当浏览器无法完成任何进一步的任务时我们可能得到错误, 或者动画处理已经完成了。我们可以用与停止超时相同的方法来实现这一点——通过将设置间隔()调用返回的标识符传递给清除间隔/*clearInterval*/()函数:

常 创建钟表 = 设置间隔(显示时间, 1000);
清除间隔(创建钟表);

关于 设置超时() 和 设置间隔() 需要注意的几点

当使用 设置超时() 和 设置间隔()的时候,有几点需要额外注意。 现在让我们回顾一下:

递归的超时

还有另一种方法可以使用设置超时():我们可以递归调用它来重复运行相同的代码,而不是使用设置间隔()

下面的示例使用递归设置超时()每100毫秒运行传递来的函数:

  定 甲 = 1;

  设置超时(务 运行() {
    控制台.日志(甲);
    甲++;
    设置超时(运行, 100);
  }, 100);

将上面的示例与下面的示例进行比较 ––这使用 设置间隔() 来实现相同的效果:

  定 甲 = 1;

  设置间隔(务 运行() {
    控制台.日志(甲);
    甲++;
  }, 100);

递归 设置超时() 和 设置间隔() 有何不同?

上述代码的两个版本之间的差异是微妙的。

  • 递归 设置超时() 保证执行之间的延迟相同,例如在上述情况下为100毫秒。 代码将运行,然后在它再次运行之前等待100毫秒,因此无论代码运行多长时间,间隔都是相同的。
  • 使用 设置间隔() 的示例有些不同。 我们选择的间隔包括执行我们想要运行的代码所花费的时间。假设代码需要40毫秒才能运行 – 然后间隔最终只有60毫秒。
  • 当递归使用 设置超时() 时,每次迭代都可以在运行下一次迭代之前计算不同的延迟。 换句话说,第二个参数的值可以指定在再次运行代码之前等待的不同时间(以毫秒为单位)。

当你的代码有可能比你分配的时间间隔,花费更长时间运行时,最好使用递归的 设置超时() – 这将使执行之间的时间间隔保持不变,无论代码执行多长时间,你不会得到错误。

立即超时

使用0用作设置超时()的回调函数将尽可能快地执行,但是在主线程代码运行之后执行。

举个例子,下面的代码在控制台显示”你好”,然后再显示“世界”。

  设置超时(务() {
    控制台.日志('世界');
  }, 0);

  控制台.日志('你好');

如果您希望设置一个代码块以便在所有主线程完成运行后立即运行,这将很有用。将其放在异步事件循环中,这样它将随后直接运行。

使用 清除超时() 或 清除间隔()清除

清除超时() 和清除间隔() 都使用相同的条目列表进行清除。有趣的是,这意味着你可以使用任一一种方法来清除 设置超时() 和 设置间隔()

但为了保持一致性,你应该使用 清除超时() 来清除 设置超时() 条目,使用 清除间隔() 来清除 设置间隔() 条目。 这样有助于避免混乱。

结论

就是这样-异步循环和间隔的所有要点在一篇文章中介绍了。您会发现这些方法在许多情况下都很有用,但请注意不要过度使用它们!因为它们仍然在主线程上运行,所以繁重的回调(尤其是那些操纵DOM的回调)会在不注意的情况下降低页面的速度。