如何实现一个同步的 sleep 函数?

探讨在 JavaScript 中实现同步 sleep 函数的方法及其优缺点。

异步编程 中等 JavaScript Promise 函数

JavaScript 是单线程非阻塞的,因此通常采用异步方式实现非阻塞等待(推荐方案)。但若需同步阻塞主线程的 sleep,可通过忙等待循环实现。以下是两种方案:

1. 同步阻塞 sleep(不推荐)

循环占用主线程直至超时,导致界面冻结、交互无响应:

function sleep(ms) {
    const start = Date.now();
    while (Date.now() - start < ms) {} // 忙等待阻塞主线程
}
// 使用示例
console.log("开始等待");
sleep(1000); // 阻塞 1 秒
console.log("等待结束");

严重缺陷

  • 造成浏览器或 Node.js 主线程完全冻结
  • 阻塞事件循环和渲染,导致 UI 无响应(按钮/动画卡死)
  • 仅限特殊场景(如 Web Worker)

2. 异步 sleep(推荐方案)

通过 Promise + setTimeout 实现非阻塞等待,配合 async/await 同步化代码流程:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
// 使用示例
async function demo() {
    console.log("开始等待");
    await sleep(1000); // 暂停 1 秒但不阻塞主线程
    console.log("等待结束");
}
demo();

优势

  • 释放主线程供其他任务运行(UI 渲染、事件处理)
  • 代码逻辑保持线性可读,无回调嵌套
  • 工程扩展性强(支持传参/错误处理):
    function sleep(ms, data) {
      return new Promise(resolve => setTimeout(() => resolve(data), ms));
    }
    
    async function demo() {
      const result = await sleep(1000, "同步数据");
      console.log(result); // 输出“同步数据”
    }
    

[!WARNING] 浏览器环境中始终优先使用异步方案,同步阻塞仅作为特殊需求的备选方案。