乐趣区

关于javascript:如何使JavaScript休眠或等待

起源:https://medium.com/dev-genius,作者:Dr. Derek Austin,翻译:公众号《前端全栈开发者》

JavaScript 不具备 sleep() 函数,该函数会导致代码在复原执行之前期待指定的时间段。如果须要 JavaScript 期待,该怎么做呢?

假如您想将三则音讯记录到 Javascript 控制台,每条音讯之间要提早一秒钟。JavaScript 中没有 sleep() 办法,所以你能够尝试应用下一个最好的办法 setTimeout()

可怜的是,setTimeout() 不能像你冀望的那样失常工作,这取决于你如何应用它。你可能曾经在 JavaScript 循环中的某个点上试过了,看到 setTimeout() 仿佛基本不起作用。

问题的产生是因为将 setTimeout() 误会为 sleep() 函数,而实际上它是依照本人的一套规定工作的。

在本文中,我将解释如何应用 setTimeout(),包含如何应用它来制作一个睡眠函数,使 JavaScript 暂停执行并在间断的代码行之间期待。


浏览一下 setTimeout() 的文档,它仿佛须要一个 “ 提早 “ 参数,以毫秒为单位。

回到原始问题,您尝试调用 setTimeout(1000) 在两次调用 console.log() 函数之间期待 1 秒。

可怜的是 setTimeout() 不能这样工作:

setTimeout(1000)
console.log(1)
setTimeout(1000)
console.log(2)
setTimeout(1000)
console.log(3)

for (let i = 0; i <= 3; i++) {setTimeout(1000)
  console.log(`#${i}`)
}

这段代码的后果齐全没有提早,就像 setTimeout() 不存在一样。

回顾文档,你会发现问题在于实际上第一个参数应该是函数调用,而不是提早。毕竟,setTimeout() 实际上不是 sleep() 办法。

你重写代码以将回调函数作为第一个参数并将必须的提早作为第二个参数:

setTimeout(() => console.log(1), 1000)
setTimeout(() => console.log(2), 1000)
setTimeout(() => console.log(3), 1000)

for (let i = 0; i <= 3; i++) {setTimeout(() => console.log(`#${i}`), 1000)
}

这样一来,三个 console.log 的日志信息在通过 1000ms(1 秒)的单次延时后,会一起显示,而不是每次反复调用之间延时 1 秒的现实成果。

在探讨如何解决此问题之前,让咱们更具体地钻研一下 setTimeout() 函数。

查看 setTimeout ()

你可能曾经留神到下面第二个代码片段中应用了箭头函数。这些是必须的,因为你须要将匿名回调函数传递给 setTimeout(),该函数将在超时后运行要执行的代码。

在匿名函数中,你能够指定在超时工夫后执行的任意代码:

// 应用箭头语法的匿名回调函数。setTimeout(() => console.log("你好!"), 1000)
// 这等同于应用 function 关键字
setTimeout(function() {console.log("你好!") }, 1000)

实践上,你能够只传递函数作为第一个参数,回调函数的参数作为残余的参数,但对我来说,这仿佛素来没有正确的工作:

// 应该能用,但不能用
setTimeout(console.log, 1000, "你好")

人们应用字符串解决此问题,然而不倡议这样做。从字符串执行 JavaScript 具备安全隐患,因为任何不当行为者都能够运行作为字符串注入的任意代码。

// 应该没用,但的确有用
setTimeout(`console.log("你好")`, 1000)

那么,为什么在咱们的第一组代码示例中 setTimeout() 失败?如同咱们在正确应用它,每次都反复了 1000ms 的提早。

起因是 setTimeout() 作为同步代码执行,并且对 setTimeout() 的屡次调用均同时运行。每次调用 setTimeout() 都会创立异步代码,该代码将在给定提早后稍后执行。因为代码段中的每个提早都是雷同的(1000 毫秒),因而所有排队的代码将在 1 秒钟的单个提早后同时运行。

如前所述,setTimeout() 实际上不是 sleep() 函数,取而代之的是,它只是将异步代码排入队列以供当前执行。侥幸的是,能够应用 setTimeout() 在 JavaScript 中创立本人的 sleep() 函数。

如何编写 sleep 函数

通过 Promises,asyncawait 的性能,您能够编写一个 sleep() 函数,该函数将按预期运行。

然而,你只能从 async 函数中调用此自定义 sleep() 函数,并且须要将其与 await 关键字一起应用。

这段代码演示了如何编写一个 sleep() 函数:

const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay))

const repeatedGreetings = async () => {await sleep(1000)
  console.log(1)
  await sleep(1000)
  console.log(2)
  await sleep(1000)
  console.log(3)
}
repeatedGreetings()

此 JavaScript sleep() 函数的性能与您预期的齐全一样,因为 await 导致代码的同步执行暂停,直到 Promise 被解决为止。

一个简略的抉择

另外,你能够在第一次调用 setTimeout() 时指定减少的超时工夫。

以下代码等效于上一个示例:

setTimeout(() => console.log(1), 1000)
setTimeout(() => console.log(2), 2000)
setTimeout(() => console.log(3), 3000)

应用减少超时是可行的,因为代码是同时执行的,所以指定的回调函数将在同步代码执行的 1、2 和 3 秒后执行。

它会循环运行吗?

如你所料,以上两种暂停 JavaScript 执行的选项都能够在循环中失常工作。让咱们看两个简略的例子。

这是应用自定义 sleep() 函数的代码段:

const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay))

async function repeatGreetingsLoop() {for (let i = 0; i <= 5; i++) {await sleep(1000)
    console.log(`Hello #${i}`)
    }
}
repeatGreetingsLoop()

这是一个简略的应用减少超时的代码片段:

for (let i = 0; i <= 5; i++) {setTimeout(() => console.log(`Hello #${i}`), 1000 * i)
}

我更喜爱后一种语法,特地是在循环中应用。

总结

JavaScript 可能没有 sleep()wait() 函数,然而应用内置的 setTimeout() 函数很容易创立一个 JavaScript,只有你审慎应用它即可。

就其自身而言,setTimeout() 不能用作 sleep() 函数,然而你能够应用 asyncawait 创立自定义 JavaScript sleep() 函数。

采纳不同的办法,能够将交织的(减少的)超时传递给 setTimeout() 来模仿 sleep() 函数。之所以可行,是因为所有对 setTimeout() 的调用都是同步执行的,就像 JavaScript 通常一样。

心愿这能够帮忙你在代码中引入一些提早——仅应用原始 JavaScript,而无需内部库或框架。

祝您编码欢快!????????????????????

退出移动版