主要是理解JavaScript的一些概念,比如单线程、任务队列、同步任务、异步任务
一、什么是单线程语言
简单的说就是,同一时间只能做同一件事。
二、任务队列
既然是单线程语言,那么做的事情就要一件一件来。这就意味着所有的任务都需要排队,前一个任务做完了,才能继续下一个任务。
所有的任务又分为了两种,一是同步任务,二是异步任务。
同步任务指的是,在主线程上排队执行的任务。
异步任务指不进入主线程,而是进入任务队列的任务。只有任务队列通知主线程,某个任务可以执行了,该异步任务才会进入主线程执行。
由此可以知道,在JavaScript内部,分为两个任务序列,一个是主线程,放同步任务,里面的同步任务按顺序来执行。当全部执行完了之后,会通知任务队列的异步任务,进入主线程执行。
举个例子:
console.log(1);
setTimeout(function(){
console.log(3)
},0)
console.log(2)
在浏览器中的打印结果是
1 2 3.
也就是说,setTimeout定时器是一个异步任务。只有当同步任务,也就是打印1 2执行完以后,才会将定时器任务放到主线程上执行,打印3
用更官方一点的话说,两者的执行顺序是这样的:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
解释一下第二步,什么叫只要异步任务有了运行结果,就在任务队列中放置一个事件。就拿定时器举个例子,
比如定时器设置了1000ms之后执行,那么只有到了这个规定的1000ms后,定时器的回调函数才会进入任务队列。然后等执行栈中的同步任务执行完之后,到任务队列中去取异步任务。由此可以想得到,定时器规定的时间到后,其实并不一定会严格执行定时器中的回调函数,而是时间到后放入任务队列。具体什么时候执行回调函数,要看任务队列的顺序,和执行栈的同步任务执行情况。只是CPU处理任务很快,才会感觉好像很准时。
三、关于事件和回调函数
“任务队列”是一个事件的队列,IO设备完成一项任务,就在”任务队列”中添加一个事件,表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”,就是读取里面有哪些事件。
“任务队列”中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入”任务队列”,等待主线程读取。
就是说,比如给某个元素添加了一个点击事件,那么只有当点击了这个元素的时候,相关事件才会被放进任务队列。其他的什么mouseout、mouseover、滚动监听,键盘事件啊都是这个道理。
“任务队列”是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,”任务队列”上第一位的事件就自动进入主线程。
四、Event Loop
主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。