谁发起网络请求并监控Timer直至完成?JS单线程下的事件循环疑问
别被JS单线程的说法局限了——这里说的单线程指的是JS执行引擎本身只有一个线程,但它根本不是孤军奋战,背后的宿主环境(浏览器、Node.js这类)才是处理异步任务的关键角色:
宿主的后台线程负责异步任务的实际执行
浏览器自带一堆后台线程:网络线程、计时器线程、DOM事件线程;Node.js则依赖libuv的线程池。这些线程完全独立于JS主线程,不会占用JS的执行资源。
比如你调用fetch时,JS引擎只是把请求参数丢给浏览器的网络线程,然后立刻回到主线程继续执行其他代码,根本不用等请求完成。异步任务完成后,由宿主通知事件循环
当网络线程拿到响应、或者计时器线程倒计时结束,这些后台线程会把对应的回调函数包装成一个任务,放到JS的任务队列里。等JS主线程把当前执行栈里的所有代码都跑完(也就是空闲下来),事件循环就会从队列里取出任务,交给JS引擎执行回调。计时器的延迟计算,和主线程忙不忙没关系
调用setTimeout时,JS引擎会把延迟时间和回调交给宿主的计时器线程,这个线程会独立开始倒计时——哪怕主线程正在执行耗时任务,计时器线程也会自己算时间。到点后就把回调丢进任务队列,但主线程得等手头的活干完,才会去处理这个回调,这就是为什么有时候setTimeout的实际执行时间会比设定的延迟长。你担心的“阻塞式轮询”不存在
事件循环的轮询阶段不是让主线程停下来死等,而是宿主在主线程空闲时,主动检查有没有完成的异步任务,把它们的回调塞进任务队列。主线程的核心工作始终是执行自己的代码栈,只有栈空了才会去处理队列里的任务。
内容的提问来源于stack exchange,提问作者ritwick_ _D

