likes
comments
collection
share

"深入理解进程、线程与JavaScript的单线程、异步编程及事件循环机制"

作者站长头像
站长
· 阅读数 22

前言

在本篇教程中,我们将从线程与进程的基本概念出发,逐步深入到JavaScript的单线程机制、异步编程模式以及事件循环机制等核心内容。通过理论讲解与实例分析相结合的方式,帮助读者全面掌握JavaScript的异步编程技巧与最佳实践。

一,进程和线程

清楚的了解什么是进程什么是线程是了解V8执行机制的基础

进程(Process)

在操作系统层面,进程是系统进行资源分配和调度的一个独立单元,是应用程序运行的容器。每个进程都有自己的独立内存空间和系统资源。在JavaScript的浏览器环境中,每个打开的浏览器标签页或窗口都可以被视为一个独立的进程。

线程(Thread)

线程是进程中的一个实体,是CPU调度和分派的基本单位,它是比进程更小的独立运行的单位。线程共享进程的资源(如内存和文件句柄),但每个线程都有自己的执行栈和程序计数器。

总结

在Web浏览器中,当打开一个标签页时,浏览器会为该页面启动一个进程,该进程 内部包含多个线程,如

  • 渲染线程:负责页面的渲染。
  • JavaScript引擎线程:解析和执行JS代码。由于JS可以操作DOM,而DOM是由渲染线程管理的,因此JS引擎线程和渲染线程是互斥的,以防止竞态条件。
  • HTTP请求线程:处理网络请求

js的加载会阻塞页面的渲染,js引擎在工作时会阻塞渲染线程,渲染线程和js引擎线程不能同时工作

二,V8编程

JavaScript 在单线程环境中执行,这意味着它一次只能处理一个任务。然而,为了支持异步编程和高效利用资源,JavaScript 引擎使用了事件循环和消息队列。进程通常包含多个线程,但 JavaScript 的运行环境(如浏览器或 Node.js)为了简化编程模型和避免线程间的复杂性,选择了单线程模型。但这并不意味着 JavaScript 无法处理并发操作;相反,通过异步编程和事件循环,JavaScript 可以高效地处理多个任务,而不会阻塞用户界面或造成资源浪费。

基础执行规则

对于JS的执行规则,基础的一点就是:遇到需要耗时的代码就先挂起,先执行不耗时代码;等不耗时代码执行完成再执行耗时代码

在执行代码的时候,V8会先将不耗时的代码先执行,之后再执行耗时的代码,举个例子

var a = 1
console.log(a); // 1

setTimeout(function () {
    let b = 1
    b++
    console.log(b); // 2
}, 1000)

console.log(a); // 1

通过打印我们能得知上述打印结果是:1,1,2。这是一个V8在执行中很好的例子

JS的单线程的优点

JS的单线程特性意味着在同一时间内,只有一个JS代码块在执行。这有助于简化编程模型,避免了多线程编程中的复杂性,如锁、竞态条件等问题。同时,它也带来了性能上的优化,如减少了上下文切换的开销。但是 js的加载会阻塞页面的渲染,js引擎在工作时会阻塞渲染线程,渲染线程和js引擎线程不能同时工作依旧是一个问题。

通过事件循环(Event Loop)可以解决这个问题,它允许JS执行代码、处理用户交互、执行异步操作(如网络请求)等,而不会阻塞UI的更新。

三,异步编程

在JS中,代码分为同步代码和异步代码。同步代码按顺序直接执行,不阻塞主线程;而异步代码则会被放入任务队列中,等待主线程空闲时执行。 异步代码进一步分为微任务宏任务

  • 微任务:包括Promise.then()process.nextTick()(Node.js特有), MutationObserver等,这些任务会在当前执行栈清空后立即执行,且每个宏任务执行后,会先清空微任务队列。
  • 宏任务:包括script(整体代码)、setTimeoutsetIntervalsetImmediate(Node.js特有)、I/O操作、UI渲染等,这些任务会按照特定顺序(如浏览器或Node.js的实现)排队执行。

"深入理解进程、线程与JavaScript的单线程、异步编程及事件循环机制"

四,事件循环机制Event Loop

事件循环是异步编程的基础,通过管理任务队列来调度和执行异步任务 通过Event Loop,JS能够非阻塞地执行代码,使得在等待如网络请求、文件读写等异步操作完成时,能够继续处理其他任务。因此,深入理解Event Loop的工作原理,对于掌握JS的异步编程模式至关重要。

event-loop执行机制是面试的必考题,牢固掌握其运行机制是基本

事件循环机制基础步骤:

  1. 执行同步代码(宏任务)
  2. 同步代码执行完毕后,检查是否有异步需要执行(检查队列中是否有代码需要执行)
  3. 执行所有的微任务
  4. 微任务执行完毕后,如果有需要就会渲染页面
  5. 执行异步宏任务,开启下一次事件循环(回到第一步)

基础题

// 面试必考
console.log(1); 
new Promise((resolve, reject) => {
    console.log(2); 
    resolve()
})
    .then(() => {
        console.log(3); 
        setTimeout(() => {
            console.log(4); 
        }, 0)
    })
    
setTimeout(() => {
    console.log(5); 
    setTimeout(() => {
        console.log(6); 
    }, 0)
}, 0)
console.log(7); 

我们通过上题中的打印来洞悉V8的运行机制

详细运行机制: "深入理解进程、线程与JavaScript的单线程、异步编程及事件循环机制"

所以最后的输出结果是 1,2,7,3,5,4,6

五,async await

async await是promise.then的语法糖,它允许你使用更加优雅的方式去处理函数的执行顺序。你需要再封装一个函数,async这个函数然后用await排列你需要执行的函数顺序。

基础用法

以这个例子来说,getData()先执行,再another(),another2()。我们给每个函数中还是return了new Promise,但是在最下面使用了async await替代.then用法。

let data = null
function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            data = [1, 2, 3]
            resolve()
        }, 1000)
    })
}
function another() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            data.push(4)
            resolve()
        }, 100)
    })
}
function another2() {
    console.log(data);
}
//ES7打造
async function foo() {
    await getData()
    await another()
    another2()
}
foo()

最后执行结果:[1,2,3,4]

进阶题

在这道题中为我们还是遵循 事件循环机制的基础步骤,要知道async await是promise.then的语法糖,那么await就等同于是.then,那么分析就简单了。

  • 首先执行同步代码:script start,async2 end,promise,script end
  • 接着微任务出列:async1 end,then1,then2
  • 最后宏任务出列:setTimeout

完美解决~

console.log('script start');

async function async1() {
    await async2()
    console.log('async1 end');
}
async function async2() {
    console.log('async2 end');
}
async1()

setTimeout(function () {
    console.log('setTimeout');
}, 0)

new Promise(function (resolve, reject) {
    console.log('promise');
    resolve()
})
    .then(() => {
        console.log('then1');
    })
    .then(() => {
        console.log('then2');
    })
console.log('script end');

小结

学习JS时,理解线程与进程是基础。JS单线程机制避免了线程互斥问题,但受限于同步执行效率低且容易造成阻塞渲染的问题。为优化,引入异步机制,并通过微任务、宏任务与事件循环(Event-Loop)协同工作,高效处理并发任务,确保程序流畅执行。

"深入理解进程、线程与JavaScript的单线程、异步编程及事件循环机制"

转载自:https://juejin.cn/post/7394788843206066227
评论
请登录