事件循环(EventLoop)
JavaScript是单线程语言,JavaScript中的任务分为同步任务(sync task)和异步任务(async task)。遇到执行函数会将其放入调用栈(先进后出)中,遇到setTimeout/setInterval等异步任务时,会把它放入到消息队列中,等主线程的任务执行完成以后,再回过头执行消息队列中的异步任务,如果异步任务中仍然有异步任务,会继续放入消息队列,以此类推,便形成了一个事件循环。
同步任务:在主线程上排队。
异步任务:在任务队列中排队,等待通知进入主线程执行。
事件循环执行过程
- 所有同步任务都在主线程上执行,形成一个执行栈。
- 主线程之外存在一个任务队列,js引擎会将异步任务的完成事件存入该队列等待主线程读取。
- 执行栈中的同步任务都执行完毕后,js引擎会读取执行任务队列中的任务。
- 主线程不断循环重复以上第三步。
任务队列(task queue)
任务队列可以看做是消息队列,耗时长的任务比如读写磁盘、ajax、或者比如带有callback的事件等完成后就会在任务队列中添加事件,表示该任务可以进入执行栈。
异步任务必须指定callback,当任务队列中的事件被主线程读取时就会执行回调函数。
主线程中的堆(heap)和栈(stack)
heap and stack:
主线程运行时产生堆和栈,栈中的代码调用很多外部的API,这些API在任务队列中添加各种事件。栈中的代码执行完毕,主线程就会读取任务队列中的事件执行回调函数。
执行栈中的代码总是在读取任务队列之前被执行。
定时器(timer)
- setTimeout
- setInterval
这两个函数只有在当前执行栈清空后才会执行callback。
setTimeout(fn, 0)
含义是:fn会等到同步任务和任务队列中的任务都处理完才会被执行。
实际上:setTimeout最短延迟时间为4ms。