js线程、事件循环

前言:

javascript 是单线程语言,单线程通俗点说就是:同一个时间只能做一件事情并不能同时做多件事,举个例子就像单车道和多车道一样,经过单车道的车辆只能按按先后顺序有序通过。多车道可以多辆车并行通过。至于javascript为什么是单线程的,在我理解就是为了避免进程冲突,举个例子一个变量a,两个进程不分先后同时去修改a=1、a=2,那试问a是等于多少?所以单线程是为了解决进程冲突的问题。至于什么是事件循环?顾名思义,事件循环就是:事件按规则循环执行!废话不多说接着看。

一、线程

什么是线程?可以把线程理解为一个公司的各个部门,各个部门各任其职。

浏览器有多个执行线程:

GUI渲染线程:

主要工作是处理浏览器渲染dom、css的工作。它和js引擎线程两个线程是并不能同步执行,因为渲染时如果同步操作了dom就会引起冲突。

js引擎线程:

执行js代码逻辑,也是主线程

事件线程(异步线程):

执行所有事件,例如onClick、onBlur、onMouseout等。

定时器线程(异步线程):

setTimeout、setInterval等...

http线程(异步线程):

执行所有http请求。

EventLoop轮循线程:

主要工作就是往复询问各线程是否响应,然后放入执行队列(消息队列)中,主进程执行完后按顺序执行从执行队列(消息队列)中‘拿’回调函数放进主队列中来执行。把它理解为一个传话员。

等...

举个例子:

console.log(0)

function A(){
    console.log(1)
}
setTimeout(A,1000)


function B(){
    console.log(2)
}
ajax.get(url,B) // 假设请求很快立即返回

function C(){
    console.log(3)
}
window.addEventListener('click',C);


// 打印顺序如下

0,2,1

3在0,2之后任意时候都可以出现,因为clcik需要通过点击触发。

js在上面的执行逻辑如下:

1、执行到console.log(0),发现他不是异步任务那么直接执行它。

2、执行到setTimeout(A,1000),发现他是个计时器,属于定时器线程也同时是异步任务,那么把它交给定时器线程。

3、执行到ajax.get(url,B),发现他是个http请求,属于http线程也同时是异步任务,那么把它交给http线程。

4、执行到window.addEventListener('click',C),发现他是个事件,属于事件线程也同时是异步任务,那么把它交给事件线程。

二、事件循环

接上面,各大线程接受到了他们的任务:定时器线程在1000毫秒后把A放入执行队列(消息队列),http线程立即把B放入消息队列(比在A之前,因为A等了一秒),然后用户在2时点击了一下那么事件线程在2秒的时候吧C放进消息队列。

然后js主线程执行完了以后,(打印0)。

接着http线程有响应了,执行队列只有个B。通知EventLoop然后浏览器把B拿去执行(打印2)。

1秒后时器线程有响应了,执行队列只有个A。通知EventLoop然后浏览器把A拿去执行(打印1)。

2秒后点击了一下,执行队列只有个C。通知EventLoop然后浏览器把C拿去执行(打印3)。

然后按以上的逻辑周而复始,称之为事件循环。

三、进一步理解:

console.log(0); // 同步任务 1
setTimeout(()=>{
    console.log(1) // 异步宏任务 1
},0)

new Promise((rs)=>{
    console.log(2); // 同步任务 2
    rs()
}).then(()=>{
    console.log(3) // 异步微任务 1
});

axios.get(url,()=>{console.log(4)}); // 异步宏任务 2

console.log(5) // 同步任务 3


// 0,2,5,3,1,4

如上代码执行顺序:

同步任务 1、2、3。然后,异步微任务 1、异步宏任务 1,同时响应。根据微任务优先执行,然后在执行宏任务。异步宏任务 2,无限接近于0响应,最后执行。

所以所有的异步任务都会在主线程执行完毕之后再执行,下面用一个例子来证明一下:

const starTime = (new Date()).getTime();


setTimeout(()=>{
    const endTime = (new Date()).getTime();
    console.log(endTime-starTime)
},0)


// 0

打印0是因为主线程执行花费的时间很短无限接近于0.

const starTime = (new Date()).getTime();

let count = 0;

setTimeout(()=>{
    const endTime = (new Date()).getTime();
    console.log(endTime-starTime) 
},0)

while(count<1400000000){
    count++
}

这样呢?结果其实是:因为while是同步任务,在主线程中需要等它算到14亿的时候才会去执行队列中拿任务执行。所以就能证明,所有的异步任务都是需要在主线程执行完后再执行。setTimeout设置2秒并不是说2秒后就会被执行,它的含义是在计时器队列2秒后被丢进执行队列中,执行时间是>=2秒。

 持续更新中...欢迎指正!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值