js线程运行机制,与从底层理解为什么我们要在异步任务里面写回调函数
Posted 北海~天空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js线程运行机制,与从底层理解为什么我们要在异步任务里面写回调函数相关的知识,希望对你有一定的参考价值。
1.进程与线程
什么是进程?
我们可以在电脑的任务管理器中查看到正在运行的进程,可以认为一个进程就是在运行一个程序,比如用浏览器打开一个网页,这就是开启了一个进程。但是比如打开3个网页,那么就开启了3个进程,我们这里只研究打开一个网页即一个进程。
一个进程的运行,当然需要很多个线程互相配合,比如打开QQ的这个进程,可能同时有接收消息线程、传输文件线程、检测安全线程…所以一个网页能够正常的运行并和用户交互,也需要很多个进程之间相互配合!
总结:最重要的是一个进程中可能有多个执行不同任务的线程
带着我的总结去理解下面的的话!!!
2.浏览器当中的线程
js在设计之初就设计为单线程的即js主线程,即js的代码只能在一个线程上运行,也就说,js同时只能执行一个js任务
那么诸如onclick回调,setTimeout,Ajax这些都是怎么实现的呢?
这些属于不同的线程我只有主线程执行js代码,按照道理我并不能执行这些代码呀!
这是因为浏览器或者node(宿主环境)是多线程的即浏览器多设置了几个线程去辅助js线程的运行
浏览器有很多线程:
1.js引擎线程(js引擎有多个线程,一个主线程,其它的后台配合主线程)
作用:执行js任务(执行js代码,用户输入,网络请求)
2.ui渲染线程
作用:渲染页面(js可以操作dom,影响渲染,所以js引擎线程和UI线程是互斥的。js执行时会阻塞页面的渲染。)
3.事件轮询处理线程
作用:轮询消息队列,event loop
4.其他线程,
有 定时器触发线程 (setTimeout)、http 异步线程、浏览器事件线程 (onclick)等等。
所以异步是浏览器的两个或者两个以上线程共同完成的。比如ajax异步请求和setTimeout
我下面讲讲这些线程 ,能帮助你理解浏览器当中的代码到底是怎样运行的!
3.js引擎线程
js引擎线程,我们称为主线程,他就是我们常说的“单线程”,即运行js代码的那个线程(不包括异步代码!!!)
1 var dlz = 2;
2 setTimeout()
3 ajax()
4 console.log()
第1、4行代码是同步代码,直接在主线程中运行;第2、3行代码交给其他线程运行。
具体运行顺序与栈和执行上下文有关这里就不过多解释,理解刚才说的就行。
4.其他线程
理解其他线程之前我们先理解一下任务队列(消息队列),任务队列是一个队列它存储的是一堆异步成功后的回调函数字符串,肯定是先成功的异步的回调函数在队列的前面,后成功的在后面。
划重点:是相应的线程执行成功之后的代码即回调函数放入任务队列里面,而不是直接放在任务队列里面!!!这点可能颠覆了很多人的认知
比如setTimeout 2秒后执行一个函数,那么这个函数是在2秒后才进队列的。
定时器触发线程 (setTimeout)、http 异步线程、浏览器事件线程 (onclick)
主线程执行JS代码时,碰到异步代码,就把它交给各自相对应的线程去执行,比如:
1 var a = 2;
2 setTimeout(fun A)
3 ajax(fun B)
4 console.log()
5 dom.onclick(func C)
主线程在运行这段代码时,碰到2 setTimeout(fun A),把这行代码交给定时器触发线程去执行
碰到3 ajax(fun B),把这行代码交给http 异步线程去执行
碰到5 dom.onclick(func C) ,把这行代码交给浏览器事件线程去执行
注意: 这几个异步代码的回调函数fun A,fun B,fun C,各自的线程都会保存着的,因为需要在未来的某个时候,将回调函数交给主线程去执行
是不是瞬间理解了我们为啥要写回调函数!!!
总结:这些线程主要有两个作用:
1.执行相对应的代码
2.保存回调函数,在未来的某个时刻,通知EventLoop轮询处理线程(下面会讲)过来取相应的回调函数然后执行
5.EventLoop轮询处理线程
上面我们已经知道了,有3个东西
主线程,处理同步代码
几个异步线程,处理异步代码
消息队列,存储着异步成功后的回调函数,一个静态存储结构
这里再对消息队列其作用就是存放着未来要执行的回调函数,比如
setTimeout(() =>
console.log(222)
, 2000)
setTimeout(() =>
console.log(333)
, 3000)
在一开始,消息队列是空的,在2秒后,一个 () =>
console.log(222)
的函数进入队列,在3秒后,一个 () =>
console.log(333)
的函数进入队列,此时队列里有两个元素,主线程从队列头中挨个取出并执行。
但是新的问题又来了,我主线程怎么知道任务队列里面是否有需要执行的回调函数呢?
此时需要用到我们的EventLoop轮询处理线程
它会在执行完同步任务之后会不断的向任务队列里面询问:“啊,你这里有没有要我执行的任务呀?”
有的话根据先进先出来执行!
6.总结
通过我上面的讲解大概能理解浏览器的运行机制,顺带理解了为什么我们经常写代码要写回调函数这个问题(先运行异步任务之后里面的回调函数交给浏览器主线程去执行)
以上是关于js线程运行机制,与从底层理解为什么我们要在异步任务里面写回调函数的主要内容,如果未能解决你的问题,请参考以下文章