JavaScript采用单线程模型,通过浏览器其他线程协助实现异步操作。本文详解任务队列如何存储异步回调,以及事件循环机制如何不断检查队列并执行同步与异步任务,从而保证主线程不阻塞且能响应用户交互。
JavaScript单线程与异步协作原理
许多开发者对JavaScript的单线程特性感到困惑:既然只有一个线程,它是如何完成异步操作的?事实上,JavaScript确实运行在单线程环境中(即主线程),但它能够处理异步任务,这得益于浏览器提供的其他辅助线程的支持。那么,这些辅助线程是如何协助主线程完成异步工作的呢?关键在于任务队列(task queue)和事件循环(event loop)机制。
任务队列的工作机制
任务队列
作为单线程语言,JavaScript中定义的所有任务默认都在主线程中执行。然而,并非所有任务都会立即执行,那些不会立即执行的任务被称为异步任务;反之,立即执行的任务则称为同步任务。异步任务会被移交浏览器的其他线程处理,但主线程需要掌握这些任务的执行状态,以便进行后续操作。
打个比方,主线程准备做饭,所以下达一个异步任务去买菜,异步任务买完菜之后得告诉主线程:“我买完菜啦”,这个时候主线程才好开始做饭。
由于JavaScript是单线程,上述的“下一步操作”无法直接定义在主函数里(否则会被当作同步任务直接执行),那么这些操作应该定义在哪里呢?答案是异步任务的回调函数中。在JavaScript的异步机制中,任务队列用于维护异步任务的回调函数。这个队列存放着回调函数,它们会在主线程执行完所有同步任务后,按照先进先出的顺序依次执行。那么,执行完任务队列之后,JavaScript主线程就结束了吗?并非如此,否则网页加载完毕后,谁来处理后续的用户交互事件(例如点击事件)呢?
事件循环的执行流程
事件循环

我们通过上图来更加形象的了解Javascript的异步机制。
执行同步任务 -> 检查任务队列中是否有任务 -> [有如果则执行] -> 检查任务队列中是否有任务 -> [有如果则执行] -> ...... 可见主线程在执行完同步任务之后,会无限循环地去检查任务队列中是否有新的“任务”,如果有则执行。而这些任务包括我们在异步任务中定义的回调函数,也包括用户交互事件的回调函数。通过事件循环,Javascript不仅很好的处理了异步任务,也很好的完成了与用户交互事件的处理。因为在完成异步任务的回调函数之后,任务队列中的任务都是由事件所产生的,因此我们也把上述的循环过程叫做事件循环。
异步机制代码实践
异步机制实践
console.log('定时器去买菜吧')
setTimeout(function(){
console.log('菜买完了,主线程去做菜吧')
}, 0)
console.log('你先去买菜,我先看个世界杯')
在浏览器中执行上述代码,兴许能更好地理解Javascript的异步机制。
核心概念总结
总结
总而言之,Javascript单线程的背后有浏览器的其他线程为其完成异步服务,这些异步任务为了和主线程通信,通过将回调函数推入到任务队列等待执行。主线程所做的就是执行完同步任务后,通过事件循环,不断地检查并执行任务队列中回调函数。