JavaScript是单线程
JavaScript的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程。
单线程就意味着,所有任务都需要排队,前一个任务结束,才会执行后一个任务。
比如 setTimeOut 规定在一定时间后才执行函数,如果时间很长,会使得网络闲置,JavaScript语言设计者意识到,这时主线程完全可以选择挂起这些处于等待中的任务,先运行排在后面的任务。等到setTimeOut返回了结果,再回过头,把挂起的任务继续执行下去。
同步和异步
单线程,就是指一次只能完成一件任务,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务。但如果有一个任务的执行时间很长,比如文件的读取或者数据的请求等等,那么后面的任务就要一直等待,这就会影响用户的使用体验。
为了解决这种情况,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
同步模式:
进入主线程中执行的任务
指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,形成执行栈。
异步模式:
进入任务队列中执行的任务
指的是不进入主线程、而进入 “任务队列” (task queue)的任务,只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
执行顺序: 执行完所有的同步任务 —> 再执行任务队列中的异步任务
事件循环(Event Loop):
同步任务和异步任务分别进入不同的执行"场所"; 同步任务进入主线程,异步任务进入Event Table并注册回调函数。
当指定的事情完成时,Event Table会将这个函数移入任务队列(task quene),等待主线程的任务执行完毕;
当栈中的代码执行完毕,执行栈(call stack)中的任务为空时,就会读取任务队列(task quene)中的任务,去执行对应的回调;如此循环,就形成js的事件循环机制(Event Loop)。
执行过程:
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 遇到异步任务, 进入Event Table并注册回调函数; 等到指定的事件完成(如ajax请求响应返回, setTimeout延迟到指定时间)时,Event Table会将这个回调函数移入Event Queue。
- 当栈中的代码执行完毕,执行栈(call stack)中的任务为空时,主线程会先检查micro-task(微任务)队列中是否有任务,如果有,就将micro-task(微任务)队列中的所有任务依次执行,直到micro-task(微任务)队列为空; 之后再检查macro-task(宏任务)队列中是否有任务,如果有,则取出第一个macro-task(宏任务)加入到执行栈中,之后再清空执行栈,检查micro-task(微任务),以此循环,直到全部的任务都执行完成。