《十七》JS 中的线程机制与事件机制

进程与线程:

不管是进程,还是线程,都是操作系统层面的概念。应用程序最终都是跑在操作系统上面的。

进程 Process:

启动一次应用程序就是一个进程,它占有一片独有的内存空间。进程是操作系统管理应用程序的一种方式。

比如打开一个 VSCode 就启动了一个 VSCode 进程,打开两个记事本就启动了两个记事本进程。

操作系统是如何做到让多个进程(例如:边听歌、边写代码、边查资料)同时工作的呢?
如果是多核 CPU 的话,是真正可以做到并行工作的。
如果是单核 CPU 的话,是因为 CPU 的运算速度非常快,可以在多个进程之间快速地切换,当进程中的线程获取到时间片时,就可以快速地执行代码。对于用户来说是感受不到这种快速的切换的。

请添加图片描述

多进程与单进程:

多进程程序:有的程序是可以启动多个进程的,这种程序称为多进程程序。

单进程程序:有,的程序只可以启动一个进程,这种程序称为单进程程序。

线程 Thread:

在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,进程内的这些“子任务”就称为线程。线程是操作系统能够运行调度的最小单位。

比如 Word,可以同时进行打字、拼写检查、打印等事情。

请添加图片描述
请添加图片描述

多线程与单线程:

多线程程序:一个进程内可以有多个线程,这种程序称为多线程程序。多线程能有效提升 CPU 的利用率,但是会产生创建多线程开销、线程间切换开销、死锁与状态同步的问题。

单线程程序:一个进程内只可以有一个线程,这种程序称为单线程程序。单线程没有创建多线程开销、没有线程间切换开销、不会出现因为线程之间争夺资源导致的死锁与状态同步的问题,但是效率低。

进程与线程的关系:

由于每个进程至少要干一件事,所以,一个进程至少有一个线程,被称为主线程,进程启动后自动创建主线程。

多个进程之间的数据是不能直接共享的(因为进程间的内存是相互独立的);但是一个进程内的多个线程是可以共享其同一进程内的数据的。

应用程序必须运行在某个进程的某个线程上。如何调度进程和线程,完全由操作系统决定,应用程序不能自己决定。

JS 是单线程的:

目前大多数浏览器都是多进程的,每个 Tab 标签页都是一个进程,这是为了防止一个页面卡死造成所有页面都无法响应,整个浏览器都需要强制退出的情况。每个进程中又有很多的线程,但是都只有一条单独的线程是用来执行 JS 代码的。因此,JS 是一门单线程的语言。

但是,在 H5 中开启 Web Workers 后可以进行多线程运行。

因为 JS 的单线程,在同一时刻只能做一件事。因此所有任务需要排队,前一个任务结束,才会执行后一个任务。如果某个任务耗时过长,就会阻塞 JS 线程。

// 这个长时间的 for 循环会阻塞 JS 线程,直到 for 循环完成才能执行下面的代码
for (let i = 0; i < 1000000; i++) {
	console.log(i)
}
console.log('Hello')

JS 为什么是单线程?

JS 作为浏览器的脚本语言,主要用来实现与用户的交互以及操作 DOM,如果 JS 是多线程的话,会带来很严重的同步任务。

例如:一个线程在一个 DOM 节点中增加内容,另一个线程要删除这个 DOM 节点,那么这个 DOM 节点究竟是要增加还是删除呢?会带来严重的同步问题。

同步任务与异步任务:

浏览器中存在很多耗时的任务的场景,例如:网路请求、事件监听、定时器等。JS 语言的设计者意识到,这时主线程完全可以挂起处于等待中的任务,先运行排在后面的任务。等到有了结果,再回过头把挂起的任务继续执行下去。

于是,将任务分为了同步任务和异步任务。

同步任务:

同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

JS 中大部分都是同步任务。

function fun1() {
  console.log(1)
}
function fun2() {
  console.log(2)
}
fun1()
fun2()

在这里插入图片描述

异步任务:

异步任务是指不进入主线程,而进入任务队列的任务,只有等到主线程中的所有同步任务都执行完毕,完成了的异步任务才会进入主线程中执行。

任务队列是一个先进先出的数据结构。

// setTimeout 函数本身和传入的回调函数还是由 JavaScript 线程来执行的,但是计时操作是由浏览器中的其他线程来进行的,而不是 JavaScript 线程,因此不会阻塞 JavaScript 线程,会继续执行下面的代码
// setTimeout 函数本身的执行立即结束,但是并不会执行传入的回调函数,而是去执行后面的 console.log();由浏览器中的其他线程来进行计时,直到计时结束,浏览器会将传入的回调函数放到任务队列中,取出执行
setTimeout(() => {
	console.log('计时器')
}, 10000)
console.log('World')

异步任务分为宏任务(macro-task)和微任务(micro-task)。在执行任何一个宏任务之前,都会先检查一下微任务队列中是否有任务,如果有的话,会优先执行完微任务队列中的任务才执行宏任务。

  1. 宏任务:setTimeout、setInterval、Ajax、DOM 事件监听。

    setTimeout(()=>{
    	console.log(1)
    }, 100)
    console.log(2) // 最终打印 2 1
    
  2. 微任务:Promise 的 then 回调(new Promise() 在实例化的过程中所执行的代码都是同步的,then 才是异步的)、queueMicrotask()(插入一个微任务)。

    new Promise(resolve => {
    	console.log(1)
    	resolve()
    }).then(() => {
    	console.log(2)
    })
    console.log(3) // 最终打印 1 3 2
    
    console.log(1)
    
    function asyncFn() {
    	return new Promise(resolve => {
    		console.log(2)
    		resolve()
    	})
    }
    
    async function fn() {
    	console.log(3)
    	// await 会暂停异步函数的执行,直到后面的表达式有了结果
    	// 也就是说,直到调用 resolve(),Promise 的状态变为已完成,下一行的代码才会执行,因此 await 下一行的代码,可以看作是 Promise.then(),是微任务
    	await asyncFn()
    	console.log(4)
    }
    
    fn()
    
    console.log(5) // 最终打印 1 3 2 5 4
    
    console.log(1)
    
    // async 异步函数默认返回的也是一个 Promise 对象
    async function asyncFn() {
    	console.log(2)
    }
    
    async function fn() {
    	console.log(3)
    	// await 会暂停异步函数的执行,直到后面的表达式有了结果
    	// 也就是说,直到调用 resolve(),Promise 的状态变为已完成,下一行的代码才会执行,因此 await 下一行的代码,可以看作是 Promise.then(),是微任务
    	await asyncFn()
    	console.log(4)
    }
    
    fn()
    
    console.log(5) // 最终打印 1 3 2 5 4
    

浏览器的事件循环机制(Event Loop):

浏览器的事件循环机制:

  1. 所有的同步任务都在主线程上执行,形成一个执行上下文栈。
  2. 主线程之外,还存在任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  3. 一旦执行上下文栈中的所有同步任务执行完毕,就会开始不断重复地从任务队列中读取事件,那些对应的异步任务,于是结束等待状态,依次进入执行上下文栈中开始执行。
// 同步任务
console.log('1')

setTimeout(() => { // 宏任务
    console.log('2')    
}, 0)

new Promise((resolve) => {	 // 同步任务
    console.log('3')
    resolve()         
    }).then(() => { // 微任务
        console.log('4')      
 }) // 打印 1 3 4 2
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值