前端同步异步-setTimeout-Promise-async-await

总结下前端的同步异步、事件循环问题,如有错误欢迎指正。

目录

一、setTimeout定时器函数

1.定义

2.基本语法

3.返回值

4.使用

1)异步执行

2)嵌套使用

3)事件循环

二、Promise

1.定义

2.状态

3.基本语法

1)resolve()

2)创建Promsie

3)处理Promise结果

(1)then回调(成功):

(2)catch(失败)

4.多个Promise组合使用

1)Promise.all

2)Promise.race

三、async/await

1.定义

2.async函数的特性

1)返回值是Promise

2)内部可以包含await关键字

3.await关键字的特性

1)暂停函数执行

2)只能在async函数内部使用

3)处理Promise结果

四、事件循环

1.定义

2.主要组成部分

1)执行栈

(1)作用

(2)原则

2)堆

3)任务队列(Task Queue)/消息队列(Message Queue)

(1)作用

(2)分类

3.执行流程

五、示例解析

1.例题一

1)输出结果

2)解析

2.例题二

1)输出结果

2)解析

3.例题三

1)输出结果

2)解析


一、setTimeout定时器函数

1.定义

是JavaScript中的一个定时器函数,主要作用是在指定的延迟时间(以毫秒为单位)后执行一次给定的函数或代码段。

2.基本语法

let timerId = setTimeout(function, delay, [arg1, arg2, ...]);

  • function:要延迟执行的函数。
  function myFunction() { console.log('Delayed message');}setTimeout(myFunction, 1000);
  • delay:延迟的时间,单位为毫秒。
  function myFunction() { console.log('Delayed message'); }setTimeout(myFunction, 1000);
  • [arg1, arg2, ...]:可选参数。
function addNumbers(a, b) { console.log(a + b); 
}setTimeout(addNumbers, 1000, 3, 5);

3.返回值

setTimeout函数会返回一个定时器的标识符(timerId)。这个标识符可用于在定时器执行之前取消定时器,通过clearTimeout函数来实现。

  let timerId = setTimeout(function() { console.log('This message will not be shown.'); }, 3000);clearTimeout(timerId);

代码通过clearTimeout函数取消了设置的定时器,console.log将不会被输出。

4.使用

1)异步执行

setTimeout是一种异步操作方式。当代码执行到setTimeout函数时,不会阻塞后续代码的执行。

console.log('Start');
setTimeout(function() { console.log('Delayed');}, 2000);
console.log('End');

在以上函数中,输出顺序是Start、End,然后2秒后输出Delayed。因为setTimeout中的函数被放入了宏任务队列,等当前执行栈中的代码执行完后,才会在2秒后执行setTimeout中的函数。

2)嵌套使用

实现更复杂的定时效果,比如模拟一个倒计时。

function countdown(seconds) {if (seconds < 0) {return;}console.log(seconds);let timerId = setTimeout(() => {countdown(seconds - 1);}, 1000);
}
countdown(5);

这个函数会从5开始每秒递减数字,直到输出0,是一个简单的倒计时器。

3)事件循环

事件循环中,setTimeout中的函数会被放入宏任务队列

二、Promise

1.定义

Promise在JavaScript中用于处理异步操作的结果。它是一个对象,用于包装一个还未完成的异步操作,可以在操作完成后进行相应的结果处理。

2.状态

  • Pending(进行中):Promise的初始状态。当一个Promise被创建但异步操作尚未完成时,处于这个状态。
  • Fulfilled(已成功):当异步操作完成时,Promise的状态变为Fulfilled。例如,一个fetch请求成功获取到数据后,对应Promise就会进入Fulfilled状态。
  • Rejected(已失败):如果异步操作出现错误,Promise就会进入Rejected状态。比如,网络请求出现错误或者读取文件失败等情况。

3.基本语法

1)resolve()

(1)调用resolve()

当在一个Promise的执行器函数(传递给new Promise()的函数)中调用resolve()时,这个Promise就会从pending(进行中)状态转变为fulfilled(已成功)状态。表明这个Promise被解决了,并且任何与该Promise关联的then()方法中的成功回调函数将会被调用,同时将resolve()的参数作为成功回调函数的参数传递进去。

(2)没有调用resolve()的情况

保持pending状态:

如果没有调用resolve(),并且也没有调用reject(),Promise会一直保持pending状态。这种状态下,与该Promise关联的then()方法中的成功回调函数不会被调用,因为Promise尚未被成功解决。

可能导致的问题:

  • 内存泄漏风险:如果有大量的Promise一直处于pending状态,并且这些Promise及其关联的对象(如回调函数)等没有被正确清理,那未完成的Promise所占用的内存空间就无法被释放,可能会导致内存泄漏。
  • 程序逻辑阻塞:某些情况下,其他依赖于这个Promise结果的代码部分可能会被无期限地阻塞等待。

2)创建Promsie

let myPromise = new Promise((resolve, reject) => {// 异步操作if (/* 异步操作成功 */1) {resolve('成功的结果');} else {reject('失败的原因');}
});

3)处理Promise结果

(1)then回调(成功):
myPromise.then((result) => { console.log(result); });

当Promise进入Fulfilled状态时,then方法中的回调函数会被调用,并传入成功的结果,可进行一些后续操作,如UI显示等。

(2)catch(失败)
myPromise.catch((error) => { console.log(error); });

当Promise进入Rejected状态时,catch中的方法会被调用,用于处理错误。

4.多个Promise组合使用

1)Promise.all

Promise.all用于同时处理多个Promise。它接受Promise数组作为参数,并返回一个新的Promise。当数组中的所有Promise都进入Fulfilled状态时,新的Promise才会进入Fulfilled状态,并且返回一个包含所有成功结果的数组。

let promise1 = new Promise((resolve) => {resolve('promise1的结果');
});
let promise2 = new Promise((resolve) => {resolve('promise2的结果');
});
Promise.all([promise1, promise2]).then((results) => {console.log(results); // ['promise1的结果', 'promise2的结果']
});

2)Promise.race

和Promise.all类似,但它只关心第一个完成的Promsie(无论成功还是失败)。当第一个Promise完成后,返回的新Promise就会采用这个最先完成的Promsie的状态和结果

let fastPromise = new Promise((resolve) => {setTimeout(() => {resolve('fastPromise的结果');}, 1000);
});
let slowPromise = new Promise((resolve) => {setTimeout(() => {resolve('slowPromise的结果');}, 2000);
});
Promise.race([fastPromise, slowPromise]).then((result) => {console.log(result); // 'fastPromise的结果'
});

三、async/await

1.定义

async/await是JavaScript中处理一步操作的一种更简洁、更易于理解的语法糖,它是基于Promise构建的。async用于定义一个异步函数,而await用于暂停异步函数的执行,直到一个Promise被解决(Fulfilled或Rejected)。

2.async函数的特性

1)返回值是Promise

一个async函数总是返回一个Promise。如果函数内部没有显式地返回一个Promise,JavaScript会自动将返回值包装在一个resolved的Promise中。

async function myAsyncFunction() {return "Hello";
}

以上代码等同于:

function myAsyncFunction() {return Promise.resolve("Hello");
}

2)内部可以包含await关键字

async函数的主要作用是可以在其中使用await来处理异步操作。不过,await只能在async函数内部使用。

3.await关键字的特性

1)暂停函数执行

当async函数执行到await关键字时,函数的执行会暂停直到后面跟着的Promise被解决

async function asyncFunction() {console.log("Before await");let result = await new Promise((resolve) => {resolve("Resolved");});console.log(result);console.log("After await");
}
asyncFunction();

调用函数中,会先输出"Before await",然后await关键字等待Promise的解决,resolve()

2)只能在async函数内部使用

await只能在async函数内部使用。如果在非async函数中使用await,会导致语法错误。这是因为await依赖于async函数提供的异步执行上下文。

3)处理Promise结果

await会获取Promise的解决值(fulfilled)或抛出异常(rejected)。

async function handlePromise() {try {let response = await fetch('https://example.com/api');let data = await response.json();return data;} catch (error) {console.error("An error occurred:", error);throw error;}
}

四、事件循环

1.定义

事件循环(Event Loop)是JavaScript的运行机制,用于处理异步任务。

它像一个永不停歇的循环,不断检查任务队列,并执行其中的任务,从而让JavaScript能够在单线程的环境下处理各种异步操作,如定时器、网络请求、用户事件等。

2.主要组成部分

1)执行栈

(1)作用

执行栈是JavaScript代码执行的地方,它是一个栈结构。当一个函数被调用,它的执行上下文会被压入执行栈。函数执行完毕后,其执行上下文会从栈顶弹出。例如,当执行

function a() { console.log('a'); } a();

时,a函数的执行上下文会被压入执行栈,执行console.log('a')后,函数执行完毕,执行上下文从栈顶弹出。

(2)原则

执行栈遵循先进后出的原则,当一个函数内部调用另一个函数时,新函数的执行上下文会被压入栈顶,等新函数执行完毕后才会弹出,然后继续执行原理函数的剩余部分。

2)堆

堆是用于存储对象和函数的内存空间。在JavaScript中,当创建一个对象或者一个函数时,这些数据会被存储在堆中。堆的内存分配和回收由JavaScript的垃圾回收机制来管理。

3)任务队列(Task Queue)/消息队列(Message Queue)

(1)作用

任务队列用于存放异步任务,如setTimeout、setInterval、Promise的then回调等。它是一个先进先出的队列结构。当异步任务可以执行时(比如setTimeout的延迟时间到了或者Promise的状态改变了),任务会被放入任务队列。

(2)分类

任务队列分为宏任务队列微任务队列。宏任务队列主要存放如setTimeout、setInterval、I/O操作、script(整体代码块)等任务;微任务队列主要存放Promise的then回调、async/await的后续操作等任务。

3.执行流程

(1)首先,JavaScript引擎会执行全局代码(script),将其压入执行栈。执行过程中,遇到同步代码,就直接执行;如果遇到异步操作,会将异步操作的相关任务交给浏览器或Node.js的其他模块(如Web API模块)去处理,执行栈继续执行其他同步代码。

(2)异步操作完成时,其对应的任务会被放入任务队列

(3)执行栈为空后,事件循环会先检查微任务队列。如果微任务队列中有任务,就将任务逐个取出并压入执行栈执行,直到微任务队列为空。

(4)微任务队列清空后,事件循环会检查宏任务队列。如果宏任务队列中有任务,就取出一个任务压入执行栈执行,这个任务执行完毕后,又回去检查微任务队列中是否有新任务(因为在宏任务执行过程中可能产生新的微任务),如此循环

五、示例解析

1.例题一

先来个比较简单的:

async function async1() {console.log('async1 start');let result = await async2();console.log(result);console.log('async1 end');
}
async function async2() {console.log('async2');
}
console.log('start');
async1();
console.log('end');

1)输出结果

2)解析

(1)执行同步代码

首先执行同步代码console.log('start'),输出'start'

(2)调用async1()

进入async1函数内部,console输出'async1 start'

await关键字异步操作,等待async2返回一个Promise,并且把Promise的返回值赋值给了result变量。

(4)执行async2()

执行async2异步函数,进入后console输出'async2'

async2执行完毕,返回一个Promise(因为没有显式地返回一个值,JavaScript会自动将其包装为一个已解决的Promise,值为undefined)

(6)回到async1函数

由于await的作用,async1函数暂停执行,等待async2返回的Promise被解决。

(7)执行同步代码

异步操作不会阻塞其他同步代码,按顺序执行console.log('end'),输出'end'

(8)回到async1

async2返回的Promise被解决(实际上它立即被解决,因为没有异步操作等待,只是一个console),async1继续执行,将async2的返回值undefined赋值给result,输出'undefined',最后输出'async1 end'

2.例题二

对例题一进行改变,得到如下:

  async function async1() {  console.log('async1 start');await async2().then((res) => {console.log(res);});console.log('async1 end');}async function async2() {  console.log('async2');return new Promise((resolve, reject) => {console.log('async2 promise');setTimeout(() => {resolve('async2 resolve');}, 2000);});}async1();console.log('end');

1)输出结果

2)解析

(1)首先执行async1()

进入async1函数,直接console.log输出'acync1 start'

遇到await async1(),开始执行async2()

(2)执行async2()

先直接console.log输出'async2'

接下来是创建一个Promise,这个Promise中直接输出'async2 promise'

遇到setTimeout定时器,会将setTimeout中的函数放入宏队列

此时async2()中的Promise的状态还没有变为fulfilled,所以async2()执行暂停并返回一个处于pending状态的Promsie。

(3)回到async1()

由于await的作用,async1函数暂停执行,等待async2返回的Promise被解决。

(4)执行console.log('end')

先执行其他同步代码,输出'end'

(5)定时器触发

事件循环检查微任务队列中没有任务,检查宏任务队列中有一个setTimeout定时器的函数,在经过2000毫秒后,setTimeout的回调函数被执行,此时,在async2中创建的Promise调用resolve('async2 resolve'),Promise的状态变为fulfilled已解决

(6)回到async1()

当async2返回的Promise被解决后,async1中的Promise的then回调函数被放入微队列任务中等待执行。

当执行栈为空时,事件循环检查微任务队列,执行then回调函数,输出res,res的值也就是之前resolve()中返回的值,输出'async2 resolve'

接着继续执行async1中await之后的代码,输出'async1 end'

3.例题三

对例题二再进行改变,得到如下:

async function async1() {console.log('async1 start');await async2().then((res) => {console.log(res);});console.log('async1 end');
}async function async2() {console.log('async2');new Promise((resolve, reject) => {console.log('async2 promise');resolve('async2 resolve');setTimeout(() => {console.log('settimeout');}, 0);}).then((res) => { console.log(res); })return await async3();
}
async function async3() {console.log('async3');return 'async3 resolve';
}
console.log('start');
async1();console.log('end');

1)输出结果

2)解析

(1)执行同步代码

console.log输出'start'

(2)执行async1()

首先输出'async1 start',然后遇到await关键字等待async2执行。

(3)执行async2()

console输出'async2',然后用new创建一个Promise对象,这个Promise中输出'async2 promise',然后调用resolve('async2 resolve')将这个Promise的状态改为了fulfilled,这时将Promise的then回调函数放入微任务队列中等待执行,也就是将console.log(res)放入微队列

接着遇到setTimeout定时器,将定时器的函数放入宏队列中,即将console.log('settimeout')放入宏队列中。

return await async3()中遇到await关键字,等待async3执行。

(4)执行async3()

在async3中直接console输出'async3',然后retuen 'async3 resolve',async函数中的return是一个Promise对象,这个对象返回值为'async3 resolve'。

(5)回到async2()

async2中获取到了async3返回的值,并且将该值return,async2函数也返回一个Promise对象,对象值是'async3 resolve'。

(6)回到async1()

这时await async2()获取到了async2函数的返回值,并且Promise状态为fulfilled已完成,所以会将Promise的回调函数then函数放入微任务队列中。

(7)执行console.log('end')

输出'end'

(8)检查微任务队列

这时微任务队列里第一个是async2函数中的Promise的then回调函数(函数作用是console.log(res)),第二个是async1函数中async2执行完毕后的then回调函数(函数作用是console.log(res))。先进先出,按照顺序执行。先输出'async2 promise',再输出'async3 resolve'。这时async1的await执行完毕,async1函数继续执行,输出'async1 end'

(9)检查宏任务队列

宏队列中只存放了async2函数中的setTimeout定时器,触发定时器,在0毫秒之后输出'settimeout'

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458084.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

矩阵概念 和 性质

目录 一、矩阵因式分解 二、矩阵在图形学的运用 一、矩阵因式分解 1、先将矩阵化为上三角阵&#xff0c;得到U 2、每个主元列以下元素 主元 得到下三角阵 二、矩阵在图形学的运用 二维移动&#xff1a; 子空间H&#xff1a; 零向量属于H 对H中任意向量u、v&#xff0c;uv…

js构造函数和原型对象,ES6中的class,四种继承方式

一、构造函数 1.构造函数是一种特殊的函数&#xff0c;主要用来初始化对象 2.使用场景 常见的{...}语法允许创建一个对象。可以通过构造函数来快速创建多个类似的对象。 const Peppa {name: 佩奇,age: 6,sex: 女}const George {name: 乔治,age: 3,sex: 男}const Mum {nam…

利用 Puppeteer-Extra 插件提升自动化测试和网页抓取的效率与隐蔽性

在当今的互联网环境中&#xff0c;自动化测试和网页抓取已经成为许多开发者和数据分析师的日常工作之一。Puppeteer 是一个广泛使用的 Node 库&#xff0c;它提供了一个高级 API 来通过 DevTools 协议控制 Chrome 或 Chromium。然而&#xff0c;在某些场景下&#xff0c;我们可…

获取微博排行榜PHP

获取微博排行榜是获取微博html页面的数据&#xff0c;而非直接调用微博后端接口获取 PHP实现 class WeiBoHotSearchService extends BaseService {/*** 微博热搜缓存过期时间* var int*/protected int $expireTime 600;/*** 微博热搜URL* var string*/protected string $doma…

centos-LAMP搭建与配置(论坛网站)

文章目录 LAMP简介搭建LAMP环境安装apache&#xff08;httpd&#xff09;安装mysql安装PHP安装php-mysql安装phpwind LAMP简介 LAMP是指一组通常一起使用来运行动态网站或者服务器的自由软件名称首字母缩写&#xff1a;Linux操作系统&#xff0c;网页服务器Apache&#xff0c;…

HTML+CSS实现超酷超炫的3D立方体相册

效果演示 HTML和CSS实现一个简单的3D立方体加载动画的相册。它使用了HTML来构建立方体的结构&#xff0c;并通过CSS来添加样式和动画效果。 HTML <div class"loader3d"><div class"cube"><div class"face"><img src&qu…

多线程——线程安全的集合类

目录 前言 一、多线程环境使用 ArrayList 1.进行加锁 2.使用 SynchronizedList 类 3.使用 CopyOnWriteArrayList 类 二、多线程环境使用队列 1.进行加锁 2.使用阻塞队列 三、多线程环境使用哈希表 1.Hashtable 2.ConcurrentHashMap &#xff08;1&#xff09;缩小锁…

计算机毕业设计 | springboot+vue凌云在线阅读平台 线上读书系统(附源码)

1&#xff0c;绪论 随着社会和网络技术的发展&#xff0c;网络小说成为人们茶钱饭后的休闲方式&#xff0c;但是现在很多网络小说的网站都是收费的&#xff0c;高额的收费制度是很多人接受不了的&#xff0c;另外就是很多小说网站都会有大量的弹窗和广告&#xff0c;这极大的影…

医学数据分析中的偏特征图可视化

在医学领域&#xff0c;我们经常需要处理复杂的数据模型&#xff0c;探索特征与目标变量之间的关系。偏特征图(Partial Dependence Plot, PDP)是一种强大的可视化技术&#xff0c;可以帮助我们更好地理解模型的行为。通过这种图形&#xff0c;我们可以直观地观察每个特征对模型…

零一万物新模型Yi-Lightning:超越GPT-4o

10月16日&#xff0c;零一万物发布了最新的旗舰模型Yi-Lightning&#xff08;闪电&#xff09;&#xff0c;在中国大模型中首度超越 GPT-4o。它在国际权威盲测榜单 LMSYS 上取得了显著成绩&#xff0c;超越了硅谷知名 OpenAI 的 GPT-4o-2024-05-13 和 Anthropic Claude 3.5 Son…

关于iPhone 16 Pro评测视频评论区特征的多维度分析

1.项目背景 随着智能手机的迅速发展&#xff0c;消费者在选择新设备时越来越依赖于网络评价和用户反馈&#xff0c;B站作为中国领先的视频分享平台&#xff0c;聚集了大量科技评测内容&#xff0c;其中UP主的评论区成为用户讨论和交流的重要场所&#xff0c;特别是在iPhone 16…

基于SSM的汽车客运站管理系统【附源码】

基于SSM的汽车客运站管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 设计原则 4.2 功能结构设计 4.3 数据库设计 4.3.1 数据库概念设计 4.3.2 数据库物理设计 5 系统实现 5.1 管理员功能实现 5.1.1 管理员信息 5.1.2 车…

【程序员的逆袭】:在失业的阴影下寻找光明

故事摘要 在失业的阴霾中&#xff0c;一位程序员如何通过外包项目重燃希望之火&#xff1f;这个故事讲述了他的谋生手段&#xff0c;如何在压力之下&#xff0c;通过信息差赚取生活所需。 要点 信息的力量&#xff1a;赚钱的关键在于信息差&#xff0c;而非单纯的体力或脑力…

【轻量级聊天应用】Vocechat本地服务器部署结合cpolar异地即时通讯

文章目录 前言1. 拉取Vocechat2. 运行Vocechat3. 本地局域网访问4. 群晖安装Cpolar5. 配置公网地址6. 公网访问小结 7. 固定公网地址 前言 本文主要介绍如何在本地群晖NAS搭建一个自己的聊天服务Vocechat&#xff0c;并结合内网穿透工具实现使用任意浏览器远程访问进行智能聊天…

iTerm2 保持SSH远程连接

1、保持SSH远程连接的稳定&#xff0c;防止因闲置时间过长而断开连接 When idle, send ASCII code 35 every 60 seconds每60秒 输入# 2、客户端设置保持活动 设置客户端每隔60秒发送一次保活信号&#xff0c;总共尝试3次。 vim ~/.ssh/configHost *ServerAliveInterval 60…

python csv库

python csv库 水一水又是一篇&#xff0c;乐 读取 import csv # 打开 CSV 文件 with open(example.csv, moder, newline) as file: csv_reader csv.reader(file) # 读取文件头&#xff08;可选&#xff09; headers next(csv_reader) print(f"Headers: {heade…

w001基于SpringBoot的在线拍卖系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

gateway 整合 spring security oauth2

微服务分布式认证授权方案 在分布式授权系统中&#xff0c;授权服务要独立成一个模块做统一授权&#xff0c;无论客户端是浏览器&#xff0c;app或者第三方&#xff0c;都会在授权服务中获取权限&#xff0c;并通过网关访问资源 OAuth2的四种授权模式 授权码模式 授权服务器将授…

【密码学】全同态加密张量运算库解读 —— TenSEAL

项目地址&#xff1a;https://github.com/OpenMined/TenSEAL 论文地址&#xff1a;https://arxiv.org/pdf/2104.03152v2 TenSEAL 是一个在微软 SEAL 基础上构建的用于对张量进行同态加密操作的开源Python库&#xff0c;用于在保持数据加密的状态下进行机器学习和数据分析。 Ten…

CSS基础—网页布局(重点!)

1、两列布局 &#xff08;1&#xff09;概念 经典两列布局是指一种网页布局方式&#xff0c;其中一列宽度固定&#xff0c;另一列宽度自适应。‌ 这种布局方式在网页设计中非常常见&#xff0c;因为它能够提供良好的视觉效果和用户体验。 如图所示&#xff1a; 页面顶部放置一…