单线程问题
首先,JavaScript是一个单线程的脚本语言,是因为如果它是多线程的话,一个线程对DOM对象进行赋值,一个线程对DOM对象进行删除,那么这个DOM对象是赋值还是删除,会带来一系列复杂的问题,所以JavaScript是单线程的。
同步任务和异步任务
首先,为什么会有同步和异步?因为JavaScript是单线程的,所有的任务都需要排队,前一个任务执行完才能执行下一个任务,但是如果前一个任务执行的时间比较长(比如读取文件或者执行ajax操作等等),都会造成较长时间的等待,如果这个时候是同步的话,用户就只能在那干等着,严重影响用户体验。
因此,在设计JavaScript的时候就考虑到了这个问题,主线程可以不用等待ajax执行完毕然后返回数据,可以先挂起处于等待中的任务,然后执行后面的其他任务,直到ajax返回了结果,他才继续回头执行被挂起的任务。因此任务可以分为同步和异步。
同步任务,同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。
异步任务,异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务,我们的ajax也是异步来执行的。
异步机制,JavaScript是如何实现异步的呢?
首先应该重要点就是异步队列,之前也说过,异步任务是不会进入主线程的,而是进入一个任务队列,任务队列也是一个先进先出的数据结构,也是一个事件队列。等他们IO完成之后,就是在任务队列中添加一个事件。表示异步任务完成了,可以进入栈中执行。但这个时候主线程并不一定为空,所以还需要等主线程执行完其他任务的时候,才会读取任务队列,读取里面有哪些事件,然后按顺序执行。如果该任务绑定了回调函数,那么主线程在处理该事件时,就会执行回调函数中的代码,也就是执行异步任务。
单线程从从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有任务,就会等到,直到有新的任务,这就叫做任务循环,因为每个任务都是由一个事件触发的,因此也叫作事件循环
所以,总的来说,JavaScript的异步机制主要包含:
- 所有的同步任务在主线程上执行,形成一个任务栈。
- 异步任务执行完后,就会在任务队列中放置一个事件。
- 当栈中的同步任务执行完毕后,系统就会在任务队列中读取事件,开始执行。
- 主线程不断重复第三步。