1,javascript是单线程(为什么不能是多线程?如果同时操作DOM,一个要增加节点,一个要删除节点,怎么处理?)但是单线程带来的问题是,如果遇到了IO设备(in/out,输入和输出设备),就必须等待结果才能继续(比如ajax很慢),因为它不知道IO是否要执行它的方法,并且执行什么方法,只有等它有了结果才能知道是否要继续执行下面的方法。所以js的设计者想了个办法,将所有的IO设备要执行的任务先放在边上(你先在边上慢慢想要不要继续执行,想到有结果了再告诉我),而先执行接下来的任务,等任务全部做完了再看看IO是否有结果了,如果有了就去执行它的任务,没有就算了。那这就是js的两种任务:同步任务和异步任务。

同步任务:就是前一个任务执行完,才能执行后一个任务,属于主线程上的任务

异步任务:不进入主线程,而是进入“任务队列”的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,主线程才会去执行。

这里我做一个总结,关于异步执行的机制:

所有的同步任务都在主线上执行,形成一个执行栈。当遇到异步任务的时候,会先把异步任务放到一边,让它自己执行,然后继续执行同步任务。如果这时候异步任务执行有了结果,那么就把结果放到“任务队列”当中,依次排好(同是异步任务也会有先后顺序),当主线上的同步任务全部执行完毕后,js会去“任务队列”瞅一眼,如果有东西(结果)排在里面,就把那个结果对应的异步任务(这里的异步任务指的是回调函数)放到主线上面执行,那些等待状态的异步任务就进入执行栈,开始执行。然后执行完了就再去“任务队列”瞅一眼,不断的循环。只要主线程空了,它就会去读取任务队列,这就是js的运行机制。(这里的瞅一眼比较抽象,也可以理解成当执行栈清空时,任务队列会自动把最前面的推进执行栈)

 

2,“任务队列”是一个事件的队列,也可以理解为消息队列,IO设备每完成一个任务,就往“任务队列”里面添加一个事件,表示相关的异步任务可以进入执行栈了,主线程读取“任务队列“,就是读取有哪些事件,那些异步任务进入“任务队列”的前提是必须有回调函数。

回调函数就是那些被主线程挂起来的代码,当主线程开始执行异步任务,其实就是开始执行那些回调函数。

任务队列是先进先出的原则,最前面的会被优先推入主线程,而如果是定时器,主线程会先检查一下执行时间,如果到了才会推入执行栈。

 

3,主线程从“任务队列”源源不断地读取事件,这整个机制就叫做Event Loop(事件循环)

执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。

 

4,定时器

HTML5把第二个参数最小值设为4,如果设置的低于4,那么就会自动增加;setTimeout(fn,0)表示当前执行栈清空后,尽可能早的执行fn,但是这个fn始终是排在当前“任务队列”的最后,因此必须等到同步任务和“任务队列”全部执行完毕后才会执行。setTimeout只是表示n毫秒后,将事件fn插入到“任务队列”中去,但是是否n毫秒后一定会触发回调函数,这个不一定。

 

在nodejs里面也有Event Loop,这个会在nodejs里面再介绍。

========================

这里补充一个概念:微任务

异步任务也要区分两种:正常任务和微任务

所谓的正常任务就是在下一轮Event Loop执行,而微任务就是在当前Event Loop的所有任务结束后执行。

正常任务包括:setTimeout,setInterval,setImmediate,I/O,各种事件的回调函数(比如鼠标事件)

微任务通常有两个:Promise的回调函数,process.nextTick,

这也是为什么当同时执行setTimeout和promise的时候,会先打印出promise,后是setTimeout,因为promise是当前Event Loop的尾部,而setTimeout是在下一轮的头部,这个在es6入门里面promise一章节有提到。

 

posted on 2017-09-23 22:00  言先生  阅读(100)  评论(0编辑  收藏  举报