首页 > 其他分享 >前端一面之 同步 vs 异步

前端一面之 同步 vs 异步

时间:2024-07-13 17:58:58浏览次数:13  
标签:异步 console log 队列 前端 任务 vs 执行

异步 vs 同步

先看一下 下面的 demo

console.log(100)
setTimeout(function () {
    console.log(200)
}, 1000)
console.log(300) 

执行结果

100
300
200
console.log(100)
alert(200)  // 1秒钟之后点击确认
console.log(300) 

这俩到底有何区别?——
第一个示例中间的步骤根本没有阻塞接下来程序的运行,
而第二个示例却阻塞了后面程序的运行。
前面这种表现就叫做 异步(后面这个叫做 同步 ),
即不会阻塞后面程序的运行。

异步和单线程

JS 需要异步的根本原因是 JS 是单线程运行的,即在同一时间只能做一件事,不能“一心二用”。

一个 Ajax 请求由于网络比较慢,请求需要 5 秒钟。
如果是同步,这 5 秒钟页面就卡死在这里啥也干不了了。
异步的话,就好很多了,5 秒等待就等待了,其他事情不耽误做,
至于那 5 秒钟等待是网速太慢,不是因为 JS 的原因。

讲到单线程,我们再来看个真题:

讲解一下下面代码的执行结果

var a = true;
setTimeout(function(){
    a = false;
}, 100)
while(a){
    console.log('while执行了')
} 

这是一个很有迷惑性的题目,不少候选人认为100ms之后,
由于a变成了false,所以while就中止了,
实际不是这样,因为JS是单线程的,
所以进入while循环之后,
没有「时间」(线程)去跑定时器了,
所以这个代码跑起来是个死循环!

异步任务

异步任务是在主线程执行的同时,
通过回调函数或其他机制委托给其他线程或事件来处理的任务。
在执行异步任务时,主线程不会等待任务完成,
而是继续执行后续代码。包括:
在这里插入图片描述

console.log('Start');

setTimeout(() => {
  console.log('Timeout callback');
}, 1000);

console.log('End');

在上述例子中,setTimeout 是一个异步任务,
它会在1秒后将回调函数推入任务队列
而主线程不会等待这个1秒,
而是继续执行后面的 console.log(‘End’)。
当主线程的同步任务执行完成后
它会检查任务队列,
将异步任务的回调函数推入执行栈,最终输出 ‘Timeout callback’。

任务队列类型

任务队列分为宏任务队列(macrotask queue)微任务队列(microtask queue)两种。
JavaScript 引擎遵循事件循环的机制,
在执行完当前
宏任务
后,
会检查微任务队列,执行其中的微任务,
然后再取下一个宏任务执行。
这个过程不断循环,形成事件循环。

  1. 宏任务队列

所有同步任务
I/O 操作, 文件读写 数据库读写等等
setTimeout、setInterval
setImmediate(Node.js环境)
requestAnimationFrame
事件监听回调函数

  1. 微任务(Microtasks)是一些较小粒度、高优先级的任务,包括:

Promise的then、catch、finally
async/await中的代码
Generator函数
MutationObserver
process.nextTick(Node.js 环境)

任务执行过程

首先,必须要明确,在JavaScript中,所有任务都在主线程上执行
任务执行过程分为同步任务和异步任务两个阶段
异步任务的处理经历两个主要阶段
Event Table(事件表)和 Event Queue(事件队列)。
Event Table存储了宏任务的相关信息
包括事件监听和相应的回调函数。
当特定类型的事件发生时,对应的回调函数被添加到事件队列中,等待执行。
例如,你可以通过addEventListener来将事件监听器注册到事件表上:

document.addEventListener('click', function() {
  console.log('Hello world!');
});

微任务与 Event Queue 密切相关。
当执行栈中的代码执行完毕后,JavaScript引擎会不断地检查事件队列
如果队列不为空就将队列中的事件一个个取出,并执行相应的回调函数。

任务队列的执行流程可概括为:
同步任务在主线程排队执行,异步任务在事件队列排队等待进入主线程执行。
遇到宏任务则推进宏任务队列,遇到微任务则推进微任务队列。
执行宏任务,执行完毕后检查当前层的微任务并执行。
继续执行下一个宏任务,执行对应层次的微任务,直至全部执行完毕。

console.log(1);

setTimeout(() => {
    console.log(2);
}, 0);

console.log(3);

new Promise((resolve) => {
    console.log(4);
    resolve();
    console.log(5);
}).then(() => {
    console.log(6);
});

console.log(7);

执行顺序解析:1 => 3 => 4 => 5 => 7 => 6 => 2。

  1. 创建Promise实例是同步的,所以1、3、4、5、7是同步执行的。
  2. then方法是微任务,放入微任务队列中,在当前脚本执行完毕后立即发生。
  3. 同步任务执行完毕后,执行微任务队列中的微任务。
  4. 最后,setTimeout放入宏任务队列,按照先进先出的原则执行。

拓展
以下是一个使用同步I/O操作的例子,这种操作会阻塞整个程序的执行

const fs = require('fs');

function readFileSync() {
    // 打印"Start"到控制台
    console.log('Start');

    // 同步读取文件的内容
    // 在文件读取完成之前,这行代码会阻塞事件循环,其他任务无法执行
    const data = fs.readFileSync('/opt/xiaodou.txt', 'utf8');
    // 打印读取到的文件内容到控制台
    console.log('File data:', data);
    // 打印"End"到控制台
    console.log('End');
}

readFileSync();

在这个例子中,fs.readFileSync是一个同步的文件读取操作,它会阻塞事件循环,直到文件读取完成。在读取文件的过程中,其他任务无法执行,整个程序的执行被阻塞。
这段代码的执行顺序是严格线性的,整个程序会在读取文件时被阻塞,直到读取操作完成。
为了对比,可以看一下使用异步I/O操作的代码,这样的代码不会阻塞事件循环:

const fs = require('fs');

function readFileAsync() {
    console.log('Start');

    // 读取文件的异步操作,这不会阻塞事件循环
    fs.readFile('/opt/xiaodou.txt', 'utf8', (err, data) => {
        if (err) {
            console.error('Error reading file:', err);
            return;
        }
        console.log('File data:', data);
    });

    console.log('End');
}

readFileAsync();

在这个例子中,fs.readFile是一个异步的文件读取操作,
它不会阻塞事件循环,程序可以继续执行其他任务。
现在对JavaScript中的同步是不是有一些理解:
导致整个程序的执行被阻塞是同步只是暂停当前函数执行是异步
使用await关键字时,它会暂停当前异步函数的执行,等待异步操作完成,
但不会阻塞事件循环,其他任务可以继续执行。

我们来看一下阻塞整个程序的代码,不仅会阻塞当前函数,还会阻塞整个事件循环,
影响所有其他任务的执行,
咱们现在在上面的同步函数中增加一个定时器,那么你猜猜定时器还能定时执行吗?

const fs = require('fs');

function readFileSync() {
    console.log('Start');

    // 设置一个定时器,计划在1秒后执行
    setTimeout(() => {
        console.log('Timer executed');
    }, 1000);

    // 同步读取文件的操作,这会阻塞事件循环
    const data = fs.readFileSync('/opt/xiaodou.txt', 'utf8');
    console.log('File data:', data);

    console.log('End');
}

readFileSync();

在这个例子中,fs.readFileSync是一个同步操作,
它会阻塞事件循环,假设文件在1秒后无法读取完成,
那么定时器无法在预定的1秒后执行。
只有当文件读取操作完成后,定时器才会有机会执行。
图解
在这里插入图片描述

这是一个简化的时序图,事件循环的主要步骤如下:
开始事件循环:JavaScript引擎开始执行代码。
同步任务队列:引擎首先检查同步任务队列(实际上是调用栈中的任务),执行队列中的任务。
执行同步任务:引擎开始执行同步任务,直到队列为空。
检查微任务队列:同步任务执行完毕后,引擎检查微任务队列。
执行微任务:如果有微任务(如Promise的.then()回调),引擎会执行这些微任务。
检查宏任务队列:微任务执行完毕后,引擎检查宏任务队列。
执行宏任务:如果有宏任务(如setTimeout回调),引擎会执行这些宏任务。执行完毕后再次检查微任务队列。
等待新任务:如果宏任务执行完毕,引擎会等待新的任务(如新的异步操作或宏任务)。
循环:引擎会不断地循环执行上述步骤。
在这里插入图片描述
分析一下
setTimeout 为定时器, 加入宏任务中
new Promise(function(resolve){ console.log(“promise1”); resolve()}) , 这里的 promise1 是同步执行
在这里插入图片描述

总结一下

有关于执行顺序
① 分清同步 、 异步代码
注意: new Promise() 里面参数(函数)是同步执行的, 但是.then() 是加入微任务队列中
② 区别哪写是加入宏任务, 哪些加入微任务
宏任务: setTimeout、setInterval 事件监听
微任务: then后面 await 后面
③ 记住在执行宏任务前, 一定需要清空微任务队列
注意: 有一些代码中时微任务里面添加微任务, 上面的案例一

可能有一些同学不是很理解上面的第一个注意点,下面详细讲一下
在浏览器中打印一下 Promise

console.dir( Promise )

在这里插入图片描述
new Promise(): 其实 Promise 可以理解成一个构建函数,
通过 new Promise() 的方式, 创建实例
打印 Promise 我们可以知道 Promise.prototype 定义了一些方法
而 new Promise().then() 可以理解成实例对象的调用构造函数原型方法
如果你还是不懂,可以看一下我有关于原型以及Promise文章

标签:异步,console,log,队列,前端,任务,vs,执行
From: https://blog.csdn.net/m0_69551472/article/details/140355926

相关文章

  • 前端HTML+CSS实现3D炫酷相册(附源码)
    前言    利用基础的html和css实现3D相册(可自我添加照片)    本人初衷是为了验证所学的知识,顺便想逗女朋友开心......
  • Html5前端基本知识整理与回顾上篇
    今天我们结合之前上传的知识资源来回顾学习的Html5前端知识,与大家共勉,一起学习。目录介绍了解注释标签结构排版标签标题标签​编辑段落标签​编辑换⾏标签​编辑⽔平分割线⽂本格式化标签媒体标签绝对路径相对路径音频标签格式​编辑注意点视频标签格......
  • vscode操作笔记
    ctrlshifp打开命令窗口 修改vscode左边搜索框或者文件名字显示的字体的方法(设置里面不包含,只有自己改css文件)找到workbench.desktop.main.css文件,修改以下属性.monaco-workbench.part>.content{font-size:14px;font-family:'YouYuan'} 我使用的插件有ms-vscode.cppto......
  • WEB前端03-CSS3基础
    CSS3基础1.CSS基本概念CSS是CascadingStyleSheets(层叠样式表)的缩写,它是一种对Web文档添加样式的简单机制,是一种表现HTML或XML等文件外观样式的计算机语言,是一种网页排版和布局设计的技术。CSS的特点纯CSS布局与结构式HTML相结合能帮助设计师分离外观与结构,和传统的表......
  • WEB前端02-HTML5基础(02)
    7.表格标签在基本表格结构中,表格标题、项目表头和数据资料构成了表格基本结构三个要素。table标签:定义表格整体<caption>我的标题</caption>:表格的标题tr标签:定义表格的行height:设置行的高度align:设置行内容的水平对齐方式valign:设置行内容的垂直对齐方式:topmiddl......
  • 自用VsCode配置文件
    {"name":"karle","settings":"{\"settings\":\"{\\r\\n\\t\\\"liveServer.settings.CustomBrowser\\\":\\\"chrome\\\",\\r\\n\\t\\\"liveServer.settings.donot......
  • 前端大屏适配方案汇总
    ......
  • 前端JS特效第32集:jQuery空间相册梦幻效果
    jQuery空间相册梦幻效果,先来看看效果:部分核心的代码如下(全部代码在文章末尾):<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"xml:lang="en......
  • 前端JS特效第31集:jQuery九宫格顺时针抽奖代码
    jQuery九宫格顺时针抽奖代码,先来看看效果:部分核心的代码如下(全部代码在文章末尾):<!DOCTYPEhtml><html><head><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/><title>jQuery九宫格顺时针抽奖代码</title><style>#lot......
  • 尽快删除!流行前端库遭受攻击,影响 10w+ 网站
    近日,安全机构Sansec发文称流行前端库 polyfill.js受到了供应链攻击,受影响的网站超过10万个。通过CDN使用该库的网站,在特定情况下会跳转到赌*和色*网站,下面就来看看这是怎么个事!polyfill.js是什么?Polyfill.js提供的代码用于模拟浏览器中缺失的原生功能。Polyf......