来源: https://medium.com/dev-genius,做者:Dr. Derek Austin,翻译:公众号《前端全栈开发者》
JavaScript不具备 sleep()
函数,该函数会致使代码在恢复执行以前等待指定的时间段。若是须要JavaScript等待,该怎么作呢?javascript
假设您想将三则消息记录到Javascript控制台,每条消息之间要延迟一秒钟。JavaScript中没有 sleep()
方法,因此你能够尝试使用下一个最好的方法 setTimeout()
。前端
不幸的是,setTimeout()
不能像你指望的那样正常工做,这取决于你如何使用它。你可能已经在JavaScript循环中的某个点上试过了,看到 setTimeout()
彷佛根本不起做用。java
问题的产生是因为将 setTimeout()
误解为 sleep()
函数,而实际上它是按照本身的一套规则工做的。安全
在本文中,我将解释如何使用 setTimeout()
,包括如何使用它来制做一个睡眠函数,使JavaScript暂停执行并在连续的代码行之间等待。框架
浏览一下 setTimeout()
的文档,它彷佛须要一个 "延迟 "参数,以毫秒为单位。异步
回到原始问题,您尝试调用 setTimeout(1000)
在两次调用 console.log()
函数之间等待1秒。async
不幸的是 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()
不存在同样。oop
回顾文档,你会发现问题在于实际上第一个参数应该是函数调用,而不是延迟。毕竟,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(() => 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()
函数。
经过Promises,async
和 await
的功能,您能够编写一个 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)
使用增长超时是可行的,由于代码是同时执行的,因此指定的回调函数将在同步代码执行的一、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()
函数,可是你可使用 async
和 await
建立自定义JavaScript sleep()
函数。
采用不一样的方法,能够将交错的(增长的)超时传递给 setTimeout()
来模拟 sleep()
函数。之因此可行,是由于全部对setTimeout()
的调用都是同步执行的,就像JavaScript一般同样。
但愿这能够帮助你在代码中引入一些延迟——仅使用原始JavaScript,而无需外部库或框架。
祝您编码愉快! 👍💻🔥😊🖖