文章目录
- [💤 JavaScript 中的"睡眠"(JS Sleep)从入门到进阶](#💤 JavaScript 中的“睡眠”(JS Sleep)从入门到进阶)
-
- [一、为什么 JavaScript 没有 `sleep()`?](#一、为什么 JavaScript 没有
sleep()?) - 二、最传统的"伪睡眠":`setTimeout()`
- [三、用 Promise 封装一个真正的"Sleep"](#三、用 Promise 封装一个真正的“Sleep”)
- 四、理解原理:事件循环与回调队列
- [五、sleep 的一些高级用法](#五、sleep 的一些高级用法)
-
- [✅ 1. 循环中使用](#✅ 1. 循环中使用)
- [✅ 2. 控制异步请求节奏](#✅ 2. 控制异步请求节奏)
- [✅ 3. 与重试机制结合(重试fetch若干次,中间睡眠一段时间)](#✅ 3. 与重试机制结合(重试fetch若干次,中间睡眠一段时间))
- 六、一些需要避免的"假睡眠"方式(完全阻塞主线程)
- [七、总结:JS Sleep 的几种方式对比](#七、总结:JS Sleep 的几种方式对比)
- [🧩 结语](#🧩 结语)
- [一、为什么 JavaScript 没有 `sleep()`?](#一、为什么 JavaScript 没有
💤 JavaScript 中的"睡眠"(JS Sleep)从入门到进阶
在其他语言中(比如 Python 的 time.sleep() 或 C++ 的 std::this_thread::sleep_for()),让程序"暂停"几秒是一件轻而易举的事。
但在 JavaScript 中,当你尝试写下这样的代码时:
js
sleep(1000);
console.log("1秒后执行");
你会发现:JS 并没有原生的 sleep() 函数!
为什么会这样?那我们该如何优雅地"让 JavaScript 睡一会儿"?
本文将从最基础的思路讲起,一步步带你深入理解 JS 的异步模型与实现 Sleep 的多种方式。
一、为什么 JavaScript 没有 sleep()?
JavaScript 是单线程、事件驱动 的语言。
它运行在一个被称为 Event Loop(事件循环) 的机制中。所有代码(包括定时器回调、异步事件)都要排队执行。
如果存在一个"阻塞"的 sleep(),主线程就会被卡住,页面会假死、无法响应用户操作 。
所以,JS 并不允许直接阻塞执行。
👉 换句话说:
在 JS 中,"睡眠"只能是非阻塞的。
二、最传统的"伪睡眠":setTimeout()
在早期,我们常用 setTimeout() 来"延迟执行":
js
console.log("开始");
setTimeout(() => {
console.log("1秒后执行");
}, 1000);
运行结果:
开始
(1秒后)
1秒后执行
虽然实现了延迟,但语义上并不是"睡眠",而是"计划稍后执行 "。
更重要的是,这种写法不支持顺序等待,比如下面这样:
js
setTimeout(() => console.log(1), 1000);
setTimeout(() => console.log(2), 2000);
它不会阻塞第一行等待第二行,而是直接注册两个定时器。
三、用 Promise 封装一个真正的"Sleep"
我们可以借助 Promise 和 async/await 实现更像"睡眠"的效果:
js
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
使用方法:
js
async function demo() {
console.log("开始");
await sleep(1000); // 暂停1秒
console.log("1秒后执行");
}
demo();
输出:
开始
(1秒后)
1秒后执行
✅ 这种写法就很接近其他语言的 sleep() 语义。
但注意,这只是让当前 async 函数暂停,并不会阻塞整个线程。
四、理解原理:事件循环与回调队列
在 await sleep(1000) 时,sleep() 返回了一个 Promise,
await 会让出执行权,JS 引擎继续处理其他任务。
- 调用
sleep(1000),注册一个定时器; - 当前 async 函数挂起,JS 主线程继续执行别的任务;
- 1000ms 后,定时器触发,Promise 被
resolve(); - async 函数恢复执行。
也就是说,这种"暂停"只是协作式的异步等待,不会阻塞整个应用。
五、sleep 的一些高级用法
✅ 1. 循环中使用
js
async function countDown(n) {
for (let i = n; i > 0; i--) {
console.log(i);
await sleep(1000);
}
console.log("时间到!");
}
countDown(3);
输出:
3
2
1
时间到!
✅ 2. 控制异步请求节奏
有时候我们希望在发起多个请求时"限速":
js
async function fetchWithDelay(urls) {
for (const url of urls) {
const res = await fetch(url);
console.log(await res.text());
await sleep(500); // 每次请求间隔0.5秒
}
}
这种场景在爬虫、防止 API 过载时特别常见。
注意:res.text()返回的是一个Promise,我们需要等待这个Promise解析后才能获取文本内容,没有await,我们得到的将是一个Promise对象,而不是文本内容
✅ 3. 与重试机制结合(重试fetch若干次,中间睡眠一段时间)
js
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url);
return await res.json();
} catch (e) {
console.log(`第 ${i + 1} 次失败,等待 2s 重试...`);
await sleep(2000);
}
}
throw new Error("重试次数耗尽");
}
六、一些需要避免的"假睡眠"方式(完全阻塞主线程)
在 StackOverflow 上常见一些"同步 sleep"写法,例如:
js
function busySleep(ms) {
const start = Date.now();
while (Date.now() - start < ms) {}
}
⚠️ 千万不要在浏览器中使用!
它会完全阻塞主线程,让页面失去响应,CPU 100%。
七、总结:JS Sleep 的几种方式对比
| 方法 | 是否阻塞 | 可读性 | 推荐程度 |
|---|---|---|---|
setTimeout |
否 | 一般 | ⭐⭐ |
Promise + await |
否 | 高 | ⭐⭐⭐⭐ |
while busy loop |
是 | 差 | ❌ 禁止使用 |
🧩 结语
JavaScript 虽然没有原生的 sleep() 函数,但通过异步机制,我们依然可以实现优雅、非阻塞的"睡眠"。
记住一句话:
JS 的 sleep,不是"让线程睡觉",而是"让任务等待"。