众所周知,JavaScript是单线程的运行的,那么既然是单线程运行,那在JS中的异步是怎么出现的,其实很简单,JS是单线程的,但不代表浏览器也是单线程的,实际上浏览器是多进程多线程运行的,JS只是其中的一个线程,负责JS代码的处理
chromium的其它进程和前端的日常开发没有较大的关系,前端日常开发中打交道最多的就是渲染进程:
GUI线程
这个负责渲染页面,解析HTML,CSS,构建DOM树,渲染树,就是在这个进程中进行的;
JS引擎线程
负责解析JS,执行JS,比如Chrome的V8引擎,就是跑在这个线程里面的,另外,语言本身没有单线程,多线程之分,而解析这个语言的引擎是存在单线程和多线程的,因此JS引擎是单线程,所以导致JS是单线程运行,这个线程与GUI线程是互斥的,也就是如果执行JS引擎线程,那么GUI线程就暂停运行,反之亦然,原因是JS也是可以操作DOM的,如果两者同时对DOM进行操作,那么界面就会出现问题;
定时触发线程
setTimeout和setInterval就是在这个线程中运行的,也就是当这两个任务触发后,会放在定时触发线程中执行,不会阻塞JS代码的运行,;
事件触发线程
将满足触发条件的事件放入任务队列,换句话说,就是一些异步的事件满足触发条件后,该任务就会被放到事件触发线程里,等待触发;
异步HTTP请求线程
也就是处理ajax请求的线程,比如实际项目中的接口,再向后台发出请求后,该任务就会被放到这条线程里面,当接口有返回了,那么该任务又会被放到事件触发线程里,等待被触发;
==============================================================
在前端通常而言,异步场景基本只有四种,分别是:**定时器,网络请求,事件绑定,ES6 Promise,**这几种情况基本覆盖了前端绝大多数的异步场景,以定时器为例,具体看一下当一个定时器触发后,大致的机制;
简介
定时器是前端较为常用的一个函数功能,当一个定时器触发后,大致上会进行如下流程:
-
主线程也就是我们说的JS引擎线程,主线程会遍历Event Loop,来判断是否有异步任务已经就绪,需要执行;
-
web API可以理解成浏览器提供的一种能力,setTimeout就是一种延迟触发的能力,在主线程中调用了这种能力;
-
定时器线程:调用了setTimeout之后,这个任务就会被放到定时器线程;
-
事件触发线程当定时器结束之后,那么这个事件会转交到事件触发线程中;
-
任务队列触发线程最终会将这个任务放置到任务队列中;
执行过程
先看一段代码:
console.log(“1”);
setTimeout(()=>{
console.log(2);
},2000)
console.log(3)
这段代码是一个最简单的定时器
使用流程图来解释上面那段定时器代码:
-
执行console.log(1),这是一个同步任务,入栈
-
执行console.log,出栈,打印了1;
-
执行了setTimeout函数,但是发现这个web api,因此先调用了web api;
-
通知定时器触发线程,定时2秒后触发;
-
执行console.log(3),这是一个同步任务,入栈;
-
执行console.log,出栈,打印了3;
-
这时候执行栈空了,就要去检查任务队列;
-
刚开始的时候还没有到2秒钟,因此任务队列是空的,没有任务;
-
循环检查;
-
2s后发现有任务队列里有一个任务;
-
执行定时器任务;
问题
定时器其实有问题的,主要的问题体现于两点: