setTimeout+Promise+Async输出顺序?

注明:本文不涉及Nodejs执行机制

同步 && 异步

什么是异步,什么是同步,我不多说,我就通过小故事来讲讲吧。

  • 同步:你打电话去书店订书,老板说我查查,你不挂电话在等待,老板把查到的结果告诉你,这期间你不能做自己的事情

  • 异步:你打电话去书店订书,老板说我查查,回头告诉你,你把电话挂了,先去做自己的事情

JS执行机制

其实不难,JavaScript代码执行机制,我就归结为三句话

  • 1、遇到同步代码直接执行

  • 2、遇到异步代码先放一边,并且将他回调函数存起来,存的地方叫事件队列

  • 3、等所有同步代码都执行完,再从事件队列中把存起来的所有异步回调函数拿出来按顺序执行

图片

请看以下例子

console.log(1) // 同步
setTimeout(() => {console.log(2) // 异步
}, 2000);
console.log(3) // 同步
setTimeout(() => {console.log(4) // 异步
}, 0);
console.log(5) // 同步输出 : 1 3 5 4 2

图片

宏任务 && 微任务

前面说了,等所有同步代码都执行完,再从事件队列里依次执行所有异步回调函数

其实事件队列也是一个小团体,人家也有自己的规则,这就类似于学校管理着许多社团,人家自己社团内部也有人家自己的规矩。

话说回来,为什么事件队列里需要有自己的规则呢?要不你先想想为什么学校里的社团里要有自己的规则要分等级,是因为有的人能力强有的人能力弱,所以也就有了等级的高低。其实事件队列也一样,事件队列是用来存异步回调的,但是异步也分类型啊,异步任务分为宏任务微任务,并且微任务执行时机先于宏任务

那宏任务和微任务都分别有哪些呢?

宏任务

#浏览器Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

微任务

#浏览器Node
Promise.prototype.then catch finally
process.nextTick
MutationObserver

执行流程

那就来说说整体的执行的流程吧

图片

例子

大家可以根据我的解题步骤去走,基本90%的题目都是没什么压力的!!!

  • 1、标记区分异步和同步

  • 2、异步中,标记区分宏任务和微任务

  • 3、分轮数,一轮一轮慢慢走

console.log(1) // 同步
setTimeout(() => {console.log(2) // 异步:宏任务
});
console.log(3) // 同步
Promise.resolve().then(()=>{ // 异步:微任务console.log(4) 
})
console.log(5) // 同步

第一轮

  • 说明:先把同步的执行输出

  • 输出:1,3,5

  • 产生宏任务:setTimeout,产生微任务:Promise.prototype.then

第二轮

  • 说明:微任务先执行

  • 输出:4

  • 产生宏任务:无,产生微任务:无

  • 剩余宏任务:setTimeout,剩余微任务:无

第三轮(结束)

  • 说明:执行宏任务

  • 输出:2

  • 产生宏任务:无,产生微任务:无

  • 剩余宏任务:无,剩余微任务:无

第一关

想一想我刚刚说的解题思路,大家可以按照那个思路来,这道题也就是分分钟的事情啦

console.log(1)
setTimeout(() => {console.log(2)Promise.resolve().then(() => {console.log(3)})
});
console.log(4)
new Promise((resolve,reject) => {console.log(5)
}).then(() => {console.log(6)setTimeout(() => {console.log(7)})
})
console.log(8)

第一步:标记

注意:Promise的executor是同步的哦!!!

console.log(1) // 同步
setTimeout(() => {console.log(2) // 异步:宏任务 setTimeout1Promise.resolve().then(() => { // 异步:微任务 then1console.log(3)})
});
console.log(4) // 同步
new Promise((resolve,reject) => {console.log(5) // 同步resolve()
}).then(() => { // 异步:微任务 then2console.log(6)setTimeout(() => {console.log(7) // 异步:宏任务 setTimeout2})
})
console.log(8) // 同步

第二步:分轮

轮数说明输出产生剩余
第一轮执行外层同步输出1,4,5,8宏任务:setTimeout1
微任务:then2
宏任务:setTimeout1
微任务:then2
第二轮执行微任务then26宏任务:setTimeout2
微任务:无
宏任务:setTimeout1,setTimeout2
微任务:无
第三轮执行宏任务setTimeout12宏任务:无
微任务:then1
宏任务:setTimeout2
微任务:then1
第四轮执行微任务then13宏任务:无
微任务:无
宏任务:setTimeout2
微任务:无
第五轮执行宏任务setTimeout27宏任务:无
微任务:无
宏任务:无
微任务:无

第二关

大家在遇到Promise.then.then这种时,如果有点懵逼的同学,可以转换一下,下面会说到

注意:then方法会自动返回一个新的Promise,也就是return new Promise,具体的Promise源码,大家可以看我这篇看了就会,手写Promise原理,最通俗易懂的版本【阅读:1.1w,点赞:430】

setTimeout(() => {console.log(1)
}, 0)
console.log(2)
const p = new Promise((resolve) => {console.log(3)resolve()
}).then(() => {console.log(4)
}).then(() => {console.log(5)
})
console.log(6)

第一步:标记 + 转换

注意:这里的转换,只针对做题时,比较好理解,平时不要这么转换,平时这么转换是不太合适的,是错的

setTimeout(() => { // 异步:宏任务 setTimeoutconsole.log(1)
}, 0)
console.log(2) // 同步
const p = new Promise((resolve) => { // p 是 then1 执行返回的新 Promiseconsole.log(3) // 同步resolve()
}).then(() => { // 异步:微任务 then1console.log(4)// 拿着 p 重新 thenp.then(() => { // 异步:微任务 then2console.log(5)})
})
console.log(6) // 同步 6

第二步:分轮

轮数说明输出产生剩余
第一轮执行同步输出2,3,6宏任务:setTimeout
微任务:then1
宏任务:setTimeout
微任务:then1
第二轮执行微任务then14宏任务:无
微任务:then2
宏任务:setTimeout
微任务:then2
第三轮执行微任务then25宏任务:无
微任务:无
宏任务:setTimeout
微任务:无
第四轮执行宏任务setTimeout1宏任务:无
微任务:无
宏任务:无
微任务:无

第三关

再说一遍:大家在遇到Promise.then.then这种时,如果有点懵逼的同学,可以转换一下

注意:then方法会自动返回一个新的Promise,也就是return new Promise,具体的Promise源码,大家可以看我这篇看了就会,手写Promise原理,最通俗易懂的版本【阅读:1.1w,点赞:430】

new Promise((resolve,reject)=>{console.log(1)resolve()
}).then(()=>{console.log(2)new Promise((resolve,reject)=>{console.log(3)resolve()}).then(()=>{console.log(4)}).then(()=>{console.log(5)})
}).then(()=>{console.log(6)
})

第一步:标记 + 转换

注意:这里的转换,只针对做题时,比较好理解,平时不要这么转换,平时这么转换是不太合适的,是错的

const p1 = new Promise((resolve, reject) => { // p1 是 then1 执行返回的新 Promiseconsole.log(1) // 同步resolve()
}).then(() => { // 异步:微任务 then1console.log(2)const p2 = new Promise((resolve, reject) => { // p2 是 then2 执行返回的新 Promiseconsole.log(3) // then1 里的 同步resolve()}).then(() => { // 异步:微任务 then2console.log(4)// 拿着 p2 重新 thenp2.then(() => { // 异步:微任务 then3console.log(5)})})// 拿着 p1 重新 thenp1.then(() => { // 异步:微任务 then4console.log(6)})
})

第二步:分轮

轮数说明输出产生剩余
第一轮执行外层同步输出1宏任务:无
微任务:then1
宏任务:无
微任务:then1
第二轮执行微任务then12,3宏任务:无
微任务:then2、then4
宏任务:无
微任务:then2、then4
第三轮执行微任务then2,then44,6宏任务:无
微任务:then3
宏任务:无
微任务:then3
第四轮执行微任务then35宏任务:无
微任务:无
宏任务:无
微任务:无

第四关

这一关,比上一关多了一个return

前面说了,then方法会自动返回一个新的Promise,相当于return new Promise,但是如果你手动写了return Promise,那return的就是你手动写的这个Promise

new Promise((resolve, reject) => {console.log(1)resolve()
}).then(() => {console.log(2)// 多了个returnreturn new Promise((resolve, reject) => {console.log(3)resolve()}).then(() => {console.log(4)}).then(() => { // 相当于return了这个then的执行返回Promiseconsole.log(5)})
}).then(() => {console.log(6)
})

第一步:标记 + 转换

由于return的是then3执行返回的Promise,所以then4其实是then3Promise.then(),所以可转换为then3.then4

new Promise((resolve, reject) => {console.log(1) // 同步resolve()
}).then(() => { // 异步:微任务 then1console.log(2) // then1 中的 同步new Promise((resolve, reject) => {console.log(3) // then1 中的 同步resolve()}).then(() => { // 异步:微任务 then2console.log(4)}).then(() => { // 异步:微任务 then3console.log(5)}).then(() => { // 异步:微任务 then4console.log(6)})
})

第二步:分轮

轮数说明输出产生剩余
第一轮执行外层同步输出1宏任务:无
微任务:then1
宏任务:无
微任务:then1
第二轮执行微任务then12,3宏任务:无
微任务:then2、then3、then4
宏任务:无
微任务:then2、then3、then4
第三轮执行微任务then2、then3、then44,5,6宏任务:无
微任务:无
宏任务:无
微任务:无

第五关

new Promise((resolve, reject) => {console.log(1)resolve()
}).then(() => {console.log(2)new Promise((resolve, reject) => {console.log(3)resolve()}).then(() => {console.log(4)}).then(() => {console.log(5)})
}).then(() => {console.log(6)
})
new Promise((resolve, reject) => {console.log(7)resolve()
}).then(() => {console.log(8)
})

第一步:标记 + 转换

const p1 = new Promise((resolve, reject) => { // p1 是 then1 执行返回的新 Promiseconsole.log(1) // 同步resolve()
}).then(() => { // 异步:微任务 then1console.log(2)const p2 = new Promise((resolve, reject) => { // p2 是 then2 执行返回的新 Promiseconsole.log(3) // then1 里的 同步resolve()}).then(() => { // 异步:微任务 then2console.log(4)// 拿着 p2 重新 thenp2.then(() => { // 异步:微任务 then3console.log(5)})})// 拿着 p1 重新 thenp1.then(() => { // 异步:微任务 then4console.log(6)})
})new Promise((resolve, reject) => {console.log(7) // 同步resolve()
}).then(() => {  // 异步:微任务 then5console.log(8)
})

第二步:分轮

轮数说明输出产生剩余
第一轮执行外层同步输出1,7宏任务:无
微任务:then1、then5
宏任务:无
微任务:then1、then5
第二轮执行微任务then1、then52,3,8宏任务:无
微任务:then2、then4
宏任务:无
微任务:then2、then4
第三轮执行微任务then2、then44,6宏任务:无
微任务:then5
宏任务:无
微任务:then5
第四轮执行微任务then55宏任务:无
微任务:无
宏任务:无
微任务:无

第六关

其实async/await的内部实现原理,是依赖于Promise.prototype.then的不断嵌套,它在题中也是可以转换的,下面会讲到。

有兴趣的朋友可以看我这篇7张图,20分钟就能搞定的async/await原理!为什么要拖那么久【阅读量:1.8w,点赞:571】

async function async1() {console.log(1);await async2();console.log(2);
}
async function async2() {console.log(3);
}
console.log(4);
setTimeout(function () {console.log(5);
});
async1()
new Promise(function (resolve, reject) {console.log(6);resolve();
}).then(function () {console.log(7);
});
console.log(8);

第一步:标记 + 转换

注意:这里的转换,只针对做题时,比较好理解,平时不要这么转换,平时这么转换是不太合适的

console.log(4); // 同步
setTimeout(function () {console.log(5); // 异步:宏任务 setTimeout
});// async1函数可转换成
console.log(1) // 同步
new Promise((resolve, reject) => {console.log(3) // 同步resolve()
}).then(() => { // 异步:微任务 then1console.log(2)
})
// async1函数结束new Promise(function (resolve, reject) {console.log(6); // 同步resolve();
}).then(function () { // 异步:微任务 then2console.log(7);
});
console.log(8); // 同步

第二步:分轮

轮数说明输出产生剩余
第一轮执行同步输出4,1,3,6,8宏任务:setTimeout
微任务:then1、then2
宏任务:setTimeout
微任务:then1、then2
第二轮执行微任务then1、then22,7宏任务:无
微任务:无
宏任务:setTimeout
微任务:无
第三轮执行宏任务setTimeout5宏任务:无
微任务:then5
宏任务:无
微任务:无

课后作业

最后给大家布置两道作业,帮大家巩固一下本文章所学的知识,大家也可以加入我的摸鱼群,进行答案的讨论。进群点击这里进群,目前已有将近1000人加入学习,我会定时举办学习分享,模拟面试等学习活动,一起学习,共同进步!!!

第一题(思考题)

想一想下面这两个有什么区别?

// 第一种
const p = new Promise((resolve, reject) => {resolve()
}).then(() => console.log(1)).then(() => console.log(2))// 第二种
const p = new Promise((resolve, reject) => {resolve()
})
p.then(() => console.log(1))
p.then(() => console.log(2))

第二题(问题不大)

async function async1() {console.log(1);await async2();console.log(2);
}
async function async2() {console.log(3);
}new Promise((resolve, reject) => {setTimeout(() => {resolve()console.log(4)}, 1000);
}).then(() => {console.log(5)new Promise((resolve, reject) => {setTimeout(() => {async1()resolve()console.log(6)}, 1000)}).then(() => {console.log(7)}).then(() => {console.log(8)})
}).then(() => {console.log(9)
})
new Promise((resolve, reject) => {console.log(10)setTimeout(() => {resolve()console.log(11)}, 3000);
}).then(() => {console.log(12)
})

第三题(有点难度)

这道题能一分钟内做出来的找我领奖,这道题需要具备一定的Promise原理基础 + async/await原理基础才能比较轻松的答对,有兴趣的同学可以看我之前写过的文章

  • 看了就会,手写Promise原理,最通俗易懂的版本【阅读:1.1w,点赞:430】

  • 7张图,20分钟就能搞定的async/await原理!为什么要拖那么久【阅读量:1.8w,点赞:571】

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

结语

欢迎大家评论区留言

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

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

相关文章

Memcached 数据库介绍

一 Memcached 介绍 (一)Memcached 简单介绍 弊端 不能持久化 (不支持存在硬盘里) Memcached 只支持能序列化的数据类型,不支持持久化,基于Key-Value的内存缓存系统 memcached 虽然没有像redis所具备的…

宏任务及微任务

js有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件,以及执行队列中的子任务。js是单线程的(某一刻只能执行一行代码),为了让耗时带啊不阻塞其他代码运行,设计了事件循环模型。 事件循环…

学生时期学习资源同步-1 第一学期结业考试题1

原创作者:田超凡(程序员田宝宝) 版权所有,引用请注明原作者,严禁复制转载

D-Star 寻路算法

D-Star 寻路算法 下面简写 D-Star 为 D* D算法:D 算法”的名称源自 Dynamic A Star,最初由Anthony Stentz于“Optimal and Efficient Path Planning for Partially-Known Environments”中介绍。它是一种启发式的路径搜索算法, 适合面对周围环境未知或者…

把 Windows 装进 Docker 容器里

本篇文章聊聊如何在 Docker 里运行 Windows 操作系统, Windows in Docker Container(WinD)。 写在前面 我日常使用 macOS 和 Ubuntu 来学习和工作,但是时不时会有 Windows 使用的场景,不论是运行某个指定的软件&…

QThread常用相关函数、线程启动方式

一、常用相关函数 可以将常用的函数按照功能进行以下分类: 线程启动 void start() 调用后会执行run()函数,但在run()函数执行前会发射信号started(),操作系统将根据优先级参数调度线程。如果线程已经在运行,那么这个函数什么也不…

杂谈-关于如何在博客或者技术站上提问才能获得作者更高的回复意愿和交流热情

如何提问一个有效的问题 😆 首先为什么写这篇文章,由于在研究生的学习和工作过程中由于个人技术知识稍微丰富一点点也比较好学,经常会被提问或者自己提问-在博客,GitHub上,Stakflow上等等-也在和学弟学妹交流的过程中听…

C#调用Halcon出现尝试读取或写入受保护的内存,这通常指示其他内存已损坏。System.AccessViolationException

一、现象 在C#中调用Halcon,出现异常提示:尝试读取或写入受保护的内存,这通常指示其他内存已损坏。System.AccessViolationException 二、原因 多个线程同时访问Halcon中的某个公共变量,导致程序报错 三、测试 3.1 Halcon代码 其中tsp_width…

ELF-DISCOVER:大型语言模型自我构建推理结构

论文地址:https://arxiv.org/pdf/2402.03620.pdf Abstract 我们引入了SELF-DISCOVER,这是一个通用框架,用于让LLMs自我发现任务内在的推理结构,以解决对典型提示方法具有挑战性的复杂推理问题。该框架的核心是一个自我发现过程&…

测试交付类项目-文档规范

目的:为了确保项目的顺利进行和成功完成,并且为项目交付物提供准确的说明和指导。 文档提供时间:一般为产品验收完成,需求方初步确认完成后,需进行相关文档的提供,供需求方进行验收。 交付文档模板&#…

Python pip 换成国内镜像源

用 easy_install 和 pip 来安装第三方库很方便,它们的原理其实就是从Python的官方源pypi.python.org/pypi 下载到本地,然后解包安装。不过因为某些原因,访问官方的pypi不稳定,很慢甚至有些还时不时的访问不了。 跟 ubuntu 的 apt …

代码随想录算法训练营第七天|454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

题目&#xff1a;454.四数相加II 文章链接&#xff1a;代码随想录 视频链接&#xff1a;LeetCode:454.四数相加|| 题目链接&#xff1a;力扣题目链接 图释&#xff1a; // 四数相加|| int fourSumCount(vector<int>& nums1, vector<int>& nums2, vect…

项目经理到底要不要考PMP?有啥好处?

很多新手项目经理或者想要转行做项目经理的人&#xff0c;都会很快的注意到”PMP”这个证书。并且开始认真思考自己要不要考这个证书&#xff1f;以及想知道这个证书考试的具体难度、流程和内容。 先说结论&#xff1a; 值得考&#xff0c; 很容易考。 我在备考的过程中惊异…

excel批量数据导入时用poi将数据转化成指定实体工具类

1.实现目标 excel进行批量数据导入时&#xff0c;将批量数据转化成指定的实体集合用于数据操作&#xff0c;实现思路&#xff1a;使用注解将属性与表格中的标题进行同名绑定来赋值。 2.代码实现 2.1 目录截图如下 2.2 代码实现 package poi.constants;/*** description: 用…

【消息队列开发】 实现消息持久化

文章目录 &#x1f343;前言&#x1f340;消息存储格式设计&#x1f6a9;queue_data文件设计&#x1f6a9;queue_stat文件设计&#x1f6a9;拓展 &#x1f384;实现统计文件&#xff08;queue_stat&#xff09;的读写⭕总结 &#x1f343;前言 本次开发目标&#xff0c;实现消…

2024阿里云域名优惠口令大全(3月更新)

2024年阿里云域名优惠口令&#xff0c;com域名续费优惠口令“com批量注册更享优惠”&#xff0c;cn域名续费优惠口令“cn注册多个价格更优”&#xff0c;cn域名注册优惠口令“互联网上的中国标识”&#xff0c;阿里云优惠口令是域名专属的优惠码&#xff0c;可用于域名注册、续…

C# MES通信从入门到精通(1)——串口传输文件

前言: 在上位机软件开发领域,有一些工厂的mes系统需要我们通过串口发送文件的方式把一些图片或者检测数据csv文件等发送给服务器,这种方式是一些比较旧的工厂采用的方式,但是这种方式也是存在的,本文就是讲解如何使用串口发送文件详情见下文。 1、串口发送文件思路 将需…

【刷题节】美团2024年春招第一场笔试【技术】

1.小美的平衡矩阵 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();int[][] nums new int[n][n], sum new int[n][n];char[] chars;for (int i 0; i < n; i) {…

介绍Oracle的SQL调化健康检查脚本(SQLHC)

概述 Oracle提供了一个SQL调优健康检查脚本&#xff08;SQLHC&#xff09;&#xff0c;用于检查需要优化的SQL的运行环境&#xff0c;生成报告以便帮助DBA找到SQL性能不佳的原因。SQLHC是SQLT的一个子集&#xff08;我后续的文章会介绍SQLT&#xff09;&#xff0c;但SQLHC与S…

迁移学习怎么用

如果想实现一个计算机视觉应用&#xff0c;而不想从零开始训练权重&#xff0c;比方从随机初始化开始训练&#xff0c;更快的方式是下载已经训练好权重的网络结构&#xff0c;把这个作为预训练&#xff0c;迁移到你感兴趣的新任务上。ImageNet、PASCAL等等数据库已经公开在线。…