许多编程语言都提供了一个名为sleep的函数,它能够暂时停止程序的执行,持续一定的时间。然而,JavaScript并没有直接包含这样的内置功能。因此,在这篇文章中,我们将深入探讨在JavaScript中实现延迟的各种方法,同时充分考虑到这门语言的异步特性。
示例
console.log('vue')
sleep(1000)
console.log('react')
sleep函数的作用,就是在打印了vue之后再过一秒才会打印react,要实现这种功能的sleep函数,有几种方式。
do..wile循环
通过模仿传统的sleep行为实现暂停效果:
/**
* 该函数接受一个以毫秒为单位的参数,表示延迟的时间长度。
* 它通过不断检查当前时间与开始时间之间的差值,直到达到指定的延迟时间。
* @param {number} ms - 延迟的时间长度,以毫秒为单位。
*/
function sleep(ms) {
// 获取当前时间的戳
const date = Date.now();
// 初始化当前时间为 null
let currentDate = null;
// 开始一个循环,直到延迟时间结束
do {
// 获取当前时间的戳
currentDate = Date.now();
// 检查当前时间与开始时间之差是否小于延迟时间
} while (currentDate - date < ms);
}
虽然这种写法可以实现我们想要的效果,但是这种同步的实现方式会阻塞代码的执行,这在JavaScript这种异步语言中通常是不推荐的,因为它会影响到代码的响应性和性能。在阻塞期间,JavaScript引擎无法执行其他任务,这可能导致资源的浪费。而且这种实现依赖于循环检查,它的延迟时间可能不会非常精确,尤其是在高负载的环境下
setTimeout
在JavaScript中,setTimeout是一个全局函数,用于在将来的某个时间点执行一段代码。它常常用于实现延迟执行、定时任务、轮询等场景。setTimeout函数接收两个参数:一个回调函数和一个延迟时间(单位为毫秒)。在延迟时间到达之前,回调函数不会执行。一旦延迟时间到达,回调函数就会被添加到事件队列中,并在当前线程的下一个空闲时间点执行。
console.log('vue');
setTimeout(() => {
console.log('pause');
}, 1000);
console.log('react');
上面的代码的执行结果你认为会是vue、pause、react这个顺序打印么?事实并非如此,这是打印结果:
setTimeout函数并没有暂时程序的执行,浏览器瞬间打印出了vue和react,过了一秒后打印了pause。因为setTimeout是一个异步函数,意味着它不会阻塞代码的执行。
console.log('vue');
setTimeout(() => {
console.log('react');
}, 1000);
这样能实现在打印vue后过一秒再打印react。这种非阻塞性的方式简洁明了,它的实施过程不需要深入理解Promise或async/await的复杂概念。然而,这种方法并不适合处理那些异步逻辑较为复杂的任务,并且它缺少对错误的处理机制。通常,在你需要执行一系列按时间顺序排列的简单任务时,可以采用这种方式。这些任务之间不需要复杂的同步或通信。
Promise和setTimeout
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
console.log('vue');
sleep(2000).then(() => { console.log('react'); });
这种方式不会阻塞操作,而且它提供了对异步流程的精细控制。不过需要对Promise有深入的理解。当Promises链变得过长时,可能会引起一些混乱。这种技术在你需要更细致地管理时间和异步任务时尤为有用。
async/await与Promise
虽然Promise+setTimeout的写法已经可以实现我们想要的效果,但是很不美观,因此我们可以用async ... await来优化一下:
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function logFunction() {
console.log('vue');
await sleep(1000);
console.log('react');
}
这种语法非常直观,便于理解,而且不会阻塞代码的执行。不过要求你必须要熟悉async/await和Promise的概念。此外,你通常需要在模块的外部对函数进行一层“包装”。如果你正在处理多个异步任务,我强烈推荐使用这种方法,因为它是最现代和最干净的解决方案。
总结
在异步编程领域,选择合适的工具对于编写高效、可维护的代码至关重要。对于简单的延迟操作,setTimeout提供了直接且易于理解的解决方案。然而,当涉及到更复杂的异步流程,如顺序执行或错误处理时,Promise和async/await则成为了更强大的工具。尽管它们可能需要更多的学习和理解,但它们提供了更加结构化和可读的代码,从而提高了代码质量和开发效率。因此,根据具体需求和场景,选择最合适的异步处理方法对于成为一名高效的开发者至关重要。