JavaScript 实现 sleep 函数的六种方案:从同步阻塞到异步优雅

作者:纸飞机

时间:2026-10-23

浏览:452

JavaScript 原生不支持 sleep 函数,本文详细解析六种实现方案。包括基于 Date 的死循环阻塞、Promise 链式调用、Generator 配合 co、ES7 async/await、child_process 子进程同步以及 npm sleep 包。重点对比各方案在阻塞主线程、代码可读性及执行效率上的差异,帮助开发者根据场景选择最佳延时策略。

文章目录

本文介绍 JavaScript 中实现 sleep(延时)功能的多种常见方法,供开发者参考。

sleep 函数定义与必要性

一、什么是 sleep 函数?

首先探讨 sleep 函数的定义与作用。

sleep 是一种用于使程序暂停执行指定时间的函数,主要起到延时效果。

例如:


console.log('1');
sleep(5000);
console.log('2');

期望在控制台输出数字 1 后,等待 5 秒再输出数字 2。

需要注意的是,上述伪代码无法直接运行,因为 JavaScript 原生环境中并不存在 sleep 方法。

因此,本文将重点介绍几种在 JavaScript 中模拟实现 sleep 功能的技术方案。

二、为什么需要 sleep?

读者可能会疑惑:既然可以使用 setTimeout 实现定时任务,为何还要专门实现 sleep?

因为 setTimeout 基于回调函数机制,在处理多个连续延时任务时,容易导致严重的回调嵌套问题(Callback Hell)。


console.time('runTime:');
setTimeout(function(){
 console.log('1')
 setTimeout(function(){
 console.log('2');
 setTimeout(function(){
  console.log('3');
  console.timeEnd('runTime:');
 }, 2000);
 }, 3000);
}, 2000);
// 1
// 2
// 3
// runTime:: 7013.104ms

为了解决上述嵌套问题,我们寻求一种更优雅的实现方式。


sleep(2000);
console.log('1');
sleep(3000);
console.log('2');
sleep(2000);
console.log('3');
...

三、sleep 函数的具体实现

接下来,我们将分别演示几种不同的 sleep 实现方法。

基于同步阻塞的实现方案

1. 基于 Date 对象实现

该方法通过死循环不断比对当前时间与目标时间,直到超时才退出循环。


function sleep(time){
 var timeStamp = new Date().getTime();
 var endTime = timeStamp + time;
 while(true){
 if (new Date().getTime() > endTime){
  return;
 } 
 }
}
console.time('runTime:');
sleep(2000);
console.log('1');
sleep(3000);
console.log('2');
sleep(2000);
console.log('3');
console.timeEnd('runTime:');
// 1
// 2
// 3
// runTime:: 7004.301ms

缺点:

此代码并未真正让线程休眠,而是通过高强度的 CPU 计算占用资源,导致 CPU 无法处理其他任务。

其副作用是,在 sleep 执行期间,所有其他任务(包括 DOM 渲染)都会被暂停。

因此,在 sleep 过程中程序会处于“假死”状态,无法执行其他任何任务。

基于异步控制的实现方案

2. 基于 Promise 的 sleep

为了解决 AJAX 回调嵌套问题,jQuery 1.8 之后引入了 Promise 支持。但单纯使用 Promise 只是将纵向嵌套转化为横向嵌套。

最终代码结构如下:


function sleep(time){
 return new Promise(function(resolve){
 setTimeout(resolve, time);
 });
}
console.time('runTime:');
console.log('1');
sleep(1000).then(function(){
 console.log('2');
 sleep(2000).then(function(){
 console.log('3');
 console.timeEnd('runTime:');
 });
});
console.log('a');
// 1
// a
// 2
// 3
// runTime:: 3013.476ms

这种方式与之前的 setTimeout 嵌套在本质上没有区别,代码可读性依然较差。

我们进一步优化,利用 ES6 的 Generator 函数来重构上述示例。

3. 基于 Generator 函数的 sleep

通过 Generator 函数执行 sleep,并配合 co 库进行自动执行。

参考代码如下:


var co = require('co');
 
function sleep(time){
 return new Promise(function(resolve){
 setTimeout(resolve, time);
 });
}
 
var run = function* (){
 console.time('runTime:');
 console.log('1');
 yield sleep(2000);
 console.log('2');
 yield sleep(1000);
 console.log('3'); 
 console.timeEnd('runTime:');
}
 
co(run);
console.log('a');
// 1
// a
// 2
// 3
// runTime:: 3004.935ms

可以看出,整体代码结构清晰,不再存在嵌套关系,阅读体验较好。

此外,该执行过程不会导致假死,也不会阻塞其他任务的运行。

然而,该方法需要额外引入 co 执行器,因此仍存在一定瑕疵。

这并非最终解决方案,因为 ES7 带来了更先进的实现方式。

ES7 async 与子进程方案

4. 基于 async 函数的 sleep

ES7 新增的 async 函数自带执行器,因此无需借助 co 即可实现 sleep。

参考代码如下:


function sleep(time){
 return new Promise((resolve) => setTimeout(resolve, time));
}
 
async function run(){
 console.time('runTime:');
 console.log('1');
 await sleep(2000);
 console.log('2');
 await sleep(1000);
 console.log('3'); 
 console.timeEnd('runTime:');
}
 
run();
console.log('a');
 
// 1
// a
// 2
// 3
// runTime:: 3009.984ms

其运行效果与之前基于 Generator 的方案一致。

5. 使用 child_process(子进程)实现 sleep 函数

前几种方法较为简单,接下来介绍一种相对复杂的实现方式。

其原理是将 sleep 逻辑放入子进程中执行,从而不影响主进程,参考代码如下:


var childProcess = require('child_process');
var nodeBin = process.argv[0];
 
function sleep(time) {
 childProcess.execFileSync(nodeBin, ['-e', 'setTimeout(function() {}, ' + time + ');']);
 // childProcess.spawnSync(nodeBin, ['-e', 'setTimeout(function() {}, ' + time + ');']);
}
 
console.time('runTime:');
console.log('1');
sleep(1000);
console.log('2');
sleep(2000);
console.log('3');
console.timeEnd('runTime:');
 
// 1
// 2
// 3
// runTime:: 3579.093ms

上述代码通过 childProcess 对象的 execFileSync 或 spawnSync 方法创建一个同步子进程。

在该同步子进程中执行定时器,待定时器执行完毕后回收子进程,主程序继续向下执行。

第三方 npm 包方案

6. 使用 npm sleep 包

除了手动实现,npm 上已有许多现成的相关 JS 包可供使用。

我们来看看第三方库 sleep 的实现方式。


var sleep = require('sleep');
 
console.log('1');
console.time('runTime:');
sleep.sleep(2); //休眠2秒钟
console.log('2');
sleep.msleep(1000); //休眠1000毫秒
console.log('3');
sleep.usleep(1000000) //休眠1000000微秒 = 1秒
console.log('4');
console.timeEnd('runTime:');
 
// 1
// 2
// 3
// 4
// runTime:: 4014.455ms

该 sleep 包由 C++ 编写并扩展至 Node.js,性能强劲,也是一个不错的选择。

以上介绍了 sleep 的六种简单实现方式。欢迎指出问题,共同交流进步。

感兴趣的朋友可以使用在线 HTML/CSS/JavaScript 代码运行工具:http://tools.jb51.net/code/HtmlJsRun 测试上述代码的运行效果。

更多关于 JavaScript 相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》。

希望本文所述对大家 JavaScript 程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章