首先我们必须知道,javascript是单线程,即js代码始终在一个线程上执行,这个线程称为js引擎线程。而单线程就意味所有任务必须进入队列来处理。
H5提出了Web Worker标准,允许javascript创建多个线程,但是子线程是受主线程控制的,而且不能操作dom,这就尴尬了。也就是说本质上还是单线程。
那么我们经常用到的ajax是怎么实现的呢?虽然js是单线程,但是浏览器是多线程的。
浏览器的线程有:
1. UI渲染线程
2. 浏览器事件触发线程
3. Http请求线程
4. EventLoop轮询线程等
噢,原来浏览器是多线程的。聪明的同学马上就明白了,其实ajax就是委托浏览器新开了一个http请求线程。在这里要说明一下,js操作dom的时候是会影响ui渲染的,这两个线程是互斥的,所以js的执行有时会阻塞页面的渲染。
另外除了主线程之外,js还提供了一个消息队列,里面是各种需要当前程序处理的消息。新的消息进入队列的时候,会自动排在队列的尾端。这里划一个重点:“消息和回调函数相互联系”。
所有的js任务可以统分为两种:
1. 同步任务: 在主线程排队支持的任务,前一个任务执行完毕后,执行后一个任务,形成一个调用栈。栈的规则是后进先出,这里千万不要理解成调用的规则是根据栈的进出规则而定的。这里我在网上找到一个例子,可以很好的来解释调用栈的执行规则。
这里输出的结果为abc,这段代码运行时,首先 a 会被加入到调用栈的顶部,然后,因为 a 内部调用了 b,紧接着 b 被加入到调用栈的顶部,当 b 内部调用 c 的时候也是类似的。在调用 c的时候,我们的调用栈从下往上会是这样的顺序:a -> b -> c。在 c 执行完毕之后,c 被从调用栈中移除,控制流回到 b 上,调用栈会变成:a -> b,然后 b 执行完之后,调用栈会变成:a,当 a 执行完,也会被从调用栈移除。
2. 异步任务:异步任务会被主线程挂起,不进入主线程,而进入消息队列。就是劣者在前面提到的消息队列,现在可以再回到上面看一下,劣者划了一个重点的地方,异步任务必须指定回调函数。只有消息队列通知主线程,回调函数才会进入调用栈。
我们现在来总结一下:
(1)所有同步任务都在主线程上执行,形成一个调用栈。
(2)主线程之外,还存在一个”消息队列”。只要异步任务有了运行结果,就在”消息队列”之中放置一个事件。
(3)一旦”调用栈”中的所有同步任务执行完毕,系统就会读取”消息队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入调用栈,开始执行。
(4)主线程不断重复上面的第三步。
OK,主线程是通过什么来重复第三步的呢?就是劣者在开始提到的浏览器线程中的EventLoop事件轮询线程。噢,浏览器原来就是靠一个负责本身程序运行的主线程和一个负责主线程与其他进程通信的轮询线程来完成大部分工作的。
说到这里,劣者想起来ES6有一个新特性专门用来处理异步任务和回调函数嵌套。那就是Promise。
热点新闻
前端开发技术库