彻底弄懂 JavaScript 异步任务处理原理

news/2024/11/22 19:55:50/

目录

1.单线程

什么是单线程?

2.同步和异步

同步

异步

3.事件循环(EventLoop)

1.事件循环的基本概念

2.微任务/宏任务

3.宏任务和微任务的执行顺序

4.常见的面试题


1.单线程

首先我们需要明白JS是单线程的,这是为了降低程序复杂性,但同时为了多个事件能同时被处理,JS提供了异步的处理方式(其实JS本身是没有异步这一说法的,都是由执行环境所提供的)

什么是单线程?

就行马路的单向通行道一样,一排车只能一辆一辆的排队前行通过。

2.同步和异步

程序同一时间只做一件事! 场景如下:

同步

同步阻塞的概念指的是A调用B,B执行完获得结果返回给A,A在等待B执行过程中是停滞的状态,直到拿到结果才会往下执行

异步

异步与同步一样,其实也是一种消息通知机制,引用上面的比方,A调用B,无需等待B的结果,A继续往下执行,的同时B也在执行,B通过状态、通知等方式来通知A或者走回调来处理结果,做一件事的时候不用等待事情的结果,继续往下走,等有了结果再来通知我

3.事件循环(EventLoop)

1.事件循环的基本概念

💡 Tips:在js执行同步任务和异步任务的过程中,在主线程执行栈中的同步任务执行完毕就会,读取任务队列中的回调函数,将回调函数放到主线程执行栈中执行,执行完毕又会继续读取任务队列中的回调函数,放到主线程执行栈中执行,如此往复的过程称为事件循环(EventLoop);

 

2.微任务/宏任务

💡 Tips:以上内容已经讲解了同步任务和异步任务的概念,javascript将异步任务又进一步划分成了宏任务和微任务;

常见的宏任务有:Ajax请求、文件操作、setTimeout、setInterval、Dom操作、<Script>内容</Script>、

常见的微任务: Promise的then、catch、finally方法,Process.nextTick,mutationObserver;

 

3.宏任务和微任务的执行顺序

在每一个宏任务执行完毕后就会去查找是否存在微任务,如果微任务存在就将微任务执行完毕再去执行下一个宏任务,以此往复,直到所有的任务执行结束

4.常见的面试题

经典面试题
async function async1(){console.log('async1 start')await async2()console.log('async1 end')
}
async function async2(){console.log('async2')
}
console.log('script start')
setTimeout(function(){console.log('setTimeout')
},0)  
async1();
new Promise(function(resolve){console.log('promise1')resolve();
}).then(function(){console.log('promise2')
})
console.log('script end')

⛳️ 分析过程

1.定义一个异步函数 async1

2.定义一个异步函数 async2

3.打印 ‘script start’ // *1

4.定义一个定时器(宏任务,优先级低于微任务),在0ms 之后输出

5.执行异步函数 async1

  • 打印 'async1 start' // *2
  • 遇到await 表达式,执行 await 后面的 async2 //关键的地方,await后面的代码会阻塞 ,async2执行完毕后,后面的代码类似于传入then()中的回调
  • 打印 'async2' // *3
  • 返回一个 Promise,跳出 async1 函数体

6.执行 new Promise 里的语句

  • 打印 ‘promise1‘ // *
  • resolve() , 返回一个 Promise 对象,把这个 Promise 压进队列里

7.打印 ’script end’ // *

8.同步栈执行完毕

9.回到 async1 的函数体,async2 函数没有返回 Promise,所以把要等async2 的值 resolve,把 Promise 压进队列

10.执行 new Promise 后面的 .then,打印 ’promise2‘ // *6

11.回到 async1 的函数体,await 返回 Promise.resolve() ,然后打印后面的 ’async1 end‘ // *7

12.最后执行定时器(宏任务) setTimeout,打印 ’setTimeout‘ // *8

⛳️ 答案:

  • script start
  • async1 start
  • async2
  • promise1
  • script end
  • promise2
  • async1 end
  • setTimeout

async2改造一下

async function async1(){console.log('async1 start')await async2()console.log('async1 end')
}
function async2(){ // 去掉了 async 关键字console.log('async2');
}
console.log('script start')
setTimeout(function(){console.log('setTimeout')
},0)  
async1();
new Promise(function(resolve){console.log('promise1')resolve();
}).then(function(){console.log('promise2')
})
console.log('script end')

⛳️ 后续结论

  • script start
  • async1 start
  • async2
  • promise1
  • script end
  • async1 end
  • promise2
  • setTimeout

 

console.log("script start")new Promise((resolve) => {console.log("promise1")resolve('微任务1')}).then((data) => { //第一个Promise加入微任务列表console.log(data)})async function fun1() {console.log("fun1")let data = await fun2() //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调console.log(data)console.log("await堵塞")}setTimeout(() => {console.log("setTimeout 宏任务") //一个宏任务,加入宏任务队列}, 0)async function fun2() {console.log("fun2")// return "no promise"return new Promise((resolve, reject) => {resolve("fun2 微任务")})// return "await"}fun1() //代码开始执行new Promise((resolve) => {console.log("promise2")resolve("微任务2")}).then((data) => { //promise状态已经fulfilled,直接加入微任务队列console.log(data)})console.log("同步end") // 同步代码结束

⛳️ 上面代码,执行结果(这种情况是: await后面的异步函数return的是一个Promise)

  1. script start
  2. promise1
  3. fun1
  4. fun2
  5. promise2
  6. 同步end
  7. 微任务1
  8. 微任务2
  9. fun2 微任务
  10. await堵塞
  11. setTimeout 宏任务
console.log("script start")new Promise((resolve) => {console.log("promise1")resolve('微任务1')}).then((data) => { //第一个Promise加入微任务列表console.log(data)})async function fun1() {console.log("fun1")let data = await fun2() //关键的地方,await后面的代码会阻塞 ,fun2执行完毕后,后面的代码类似于传入then()中的回调console.log(data)console.log("await堵塞")}setTimeout(() => {console.log("setTimeout 宏任务") //一个宏任务,加入宏任务队列}, 0)async function fun2() {console.log("fun2")return "no promise"// return new Promise((resolve, reject) => {//     resolve("fun2 微任务")// })// return "await"}fun1() //代码开始执行new Promise((resolve) => {console.log("promise2")resolve("微任务2")}).then((data) => { //promise状态已经fulfilled,直接加入微任务队列console.log(data)})console.log("同步end") // 同步代码结束

 ⛳️ 上面代码,执行结果(这种情况是: await后面的异步函数return的不是Promise)

  1. script start
  2. promise1
  3. fun1
  4. fun2
  5. promise2
  6. 同步end
  7. 微任务1
  8. no promise
  9. await堵塞
  10. 微任务2
  11. setTimeout 宏任务

 

⚠️ 需要注意的点

  • 从结果上来看,await后面的异步函数return的是否是Promise,执行顺序的不同点在于await后面的代码执行是在微任务2之前还是之后执行,也就是微任务队列顺序的问题
  • 当return的是Promise时:await后面的代码会在微任务2之后执行
  • 当return的不是Promise时:await后面的代码会在微任务2之前执行

⚠️ 当return的不是promise时

  • async声明的异步函数会自动将返回包装成promise ,到调用fun2这个异步函数时,await就可以看做是使用then方法,会将回调函数(这里就是await后面的代码) 放入微任务队列,这里是排在微任务一之后,微任务二之前,所以这里await后面的代码会在微任务2之前执行

⚠️ 当return的是promise时

  • 当resolve的参数是一个promise时,此时会将这个参数promise放入微任务队列(此时排在微任务1之后),而他的父级promise状态不会改变,等到微任务队列中执行到这个promise时,父promise的then方法绑定到子promise上,所以这就解释了上面的执行顺序的问题

 


http://www.ppmy.cn/news/366313.html

相关文章

C++基础:二维费用的背包问题

注意&#xff1a;如果你还没搞定&#xff08;指的是真正理解&#xff09;01背包&#xff0c;请不要看。看了脑壳更晕 什么是二维费用的背包问题&#xff1f;请看AcWing上的一道题&#xff1a; 有 N 件物品和一个容量是 V 的背包&#xff0c;背包能承受的最大重量是 M。 每件物…

怎样对接同花顺股票程序化交易接口?

只要我们拿到同花顺股票程序化交易接口的客户段&#xff0c;就可以直接使用Python调用其提供的交易功能。我们可以使用Python中的Sanic异步框架将交易接口进一步封装成HTTP访问接口&#xff0c;方便从远程Linux主机调用。 对接同花顺股票程序化交易接口需要一定的编程基础&…

同花顺股票交易通过接口获取数据

同花顺股票交易通过接口获取数据

同花顺level2股票接口是什么?

一般来讲&#xff0c;同花顺level2股票接口实际上是股票定量交易的工具之一。普通投资者最多只能看到10个交易市场&#xff0c;但数据太少&#xff0c;我们很难做出交易决定。我们必须获得更多的数据来做出更准确的判断level2市场接口可以帮助我们。换句话说&#xff0c;同花顺…

使用同花顺获取单只股票的所有历史日线数据

同花顺导出股票数据 进入个股K线图界面菜单栏: 分析>历史成交右键>数据导出>导出所有数据选择路径>下一步>完成

获取同花顺正在浏览哪只股票及页面数据和代码的方法

对于获取正在浏览哪只股票&#xff0c;除了论坛里和网上公开的从内存基址中获取的方法外&#xff0c;还有另一种更直接、更方便的方法那就是网页hook。 现在很多流行软件如某信等&#xff0c;都采用软件窗口内部再内嵌一个Chromium内核&#xff08;Chromium Embedded Framework…

同花顺(股市)爬虫

爬取同花顺&#xff08;http://q.10jqka.com.cn&#xff09; 站点前20页股市数据并存储在xlsx文件 难度 中 逆向参数 hexin-v >>刷新一次会有有效次数 import copy import requests import pandas as pd from tqdm import tqdm from parsel import Selectorheaders {h…

同花顺常用公式

一&#xff1a;日k涨跌停颜色标识 进入个股日k&#xff0c;点击均线&#xff0c;点击公式修改&#xff0c;在编辑区顶部输入公式&#xff1a; CK:IF(CODELIKE(688) OR CODELIKE(300) OR CODELIKE(301),1,0);FCK:IF(CODELIKE(688) OR CODELIKE(300) OR CODELIKE(301),0,1);涨停…