【JavaScript】异步(eventloop、Promise、async/await)

本文详细介绍了JavaScript中的异步机制,包括事件循环(event loop)的工作原理、Promise的使用技巧及状态变化、async/await语法糖的特性以及宏任务与微任务的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步

1 event loop(事件循环/事件轮询)

JS 是单线程运行的

异步要基于回调来实现

event loop 就是异步回调的实现原理

1.1 JS 如何执行

  1. 从前到后,一行一行执行。如果某一行报错,则停止下面代码的执行。

  2. 先把同步代码执行完,再执行异步。异步的实现需要考虑到回调。

console.log('Hi')
setTimeout(function cb1() {
    console.log('cb1') //cb 即 callback
},5000)
console.log('Bye')

1.2 event loop 过程

  1. 同步代码,一行一行放在 Call Stack(调用栈)执行,执行完毕后会自动清空。
  2. 遇到异步,会先 “记录”下来,等待时机(定时结束、网络请求返回等)
  3. 时机到了,就将之前“记录”的内容移动到 Callback Queue中。
  4. Call Stack为空(即同步代码执行完) Event Loop开始工作,即Event Loop开始轮询查找 Callback Queue,Callback Queue中有内容则移动到Call Stack中执行。
  5. 如果callback queue中没有内容,则Event Loop会一直轮询查找(像没关机的手机一样,有电话就接,没电话是就一直开机看有没有电话)

在这里插入图片描述
这里所说的event loop 一直轮询的意思是,在call stack 清空后被触发,然后已知轮询 callback queue,当callback queue 中有内容时,将内容传给 call stack,此时call stack 中有内容,因此,轮询暂停,待call stack 没内容时再继续轮询。

console.log('a')
setTimeout(function fnc() {
    console.log('c1')
}, 5000)
console.log('b')

1.3 异步基于 event loop

setTimeout 、 ajax 等异步操作使用回调,基于 event loop

DOM 事件也使用回调,基于 event loop (DOM事件并不是异步,虽然基于的原理相同)

2 promise 进阶

三种状态: pending(正在进行中) resolved rejected

​ pending -> resolved; pending -> rejected

状态的表现和变化:

​ pending 状态,不会触发 then 和 catch

resolved 状态,会触发后续的 then 回调函数

rejected 状态,会触发后续的 catch 回调函数

then 和 catch 对状态的影响

then 正常返回 resolved ,里面有报错则返回 rejected

catch 正常返回 resolved,里面有报错则返回 rejected

2.1 then 和 catch 改变状态

  • then 正常返回 resolved ,里面有报错则返回 rejected
const p1 = Promise.resolve().then(() => {
    return 100
})
console.log('p1',p1) //resolved ,打印显示的是pending,因为异步的原因,点开Promise对象,可以看出是 resolved
p1.then(() => {
    console.log('123')//*
})

const p2 = Promise.resolve().then(() => {
    throw new Error('then error')
})
p2.then(() => {
    console.log('456')
}).catch(err => {
    console.error('error100',err)//*
})
console.log('p2',p2)   // rejeced 触发后续 catch 回调

  • catch 正常返回 resolved,里面有报错则返回 rejected(得先由 rejected 状态触发catch,才有这个结论)

    const p3 = Promise.reject('my error').catch(err => {
        console.error(err)
    })
    console.log('p3',p3) //resolved 可以触发一个 then 回调
    p3.then(() => {
        console.log(100)
    })
    
    const p4 = Promise.reject('my error').catch(err => {
        throw new Error('catch err')
    })
    console.log('p4', p4) // rejected 可以触发一个 catch 回调
    P4.then(() => {
        console.log(101)
    }).catch(err => {
        console.error('error101',err) // resolved 因为此处是catch 返回的promise
    })
    

举例:

   // 其一
   Promise.resolve().then(() => {
       console.log(1) //1
   }).catch(() => {
       console.log(2)
   }).then(() => {
       console.log(3) //3
   })
   
   
   //其二
   Promise.resolve().then(() => {
       console.log(1) //1
       throw new Error('erro1')
   }).catch(() => {
       console.log(2) //2
   }).then(() => {
       console.log(3) //3
   })
   
   //其三
   Promise.resolve().then(() => {
       console.log(1)		//1 rejeced
       throw new Error('erro1')
   }).catch(() => {
       console.log(2)	//2 resolved
   }).catch(() => {
       console.log(3)
   })

3 async/await

3.1 背景

  • 异步回调 callback hell
  • Promise then catch 链式调用,但也是基于回调函数
  • async/await 是同步语法,彻底消灭回调函数(同步语法)
; (async function ( ){
    const img1 = await loadImg(src1)
    console.log(img1.height,img1.width)
})()

async function loadImg1() {
    const img1 =await loadImg(src1)
    return img1
}

3.2 async/await 和 Promise 的关系

async/await 是消灭异步回调的终极武器,但和Promise并不互斥,反而,两者相辅相成。

  1. 执行async函数,返回的是 Promise 对象
  2. await 相当于 Promise 的 then
  3. try…catch 可捕获异常,代替了 Promise 的 catch
async function fn1() {
    //return 100  //相当于return Promise。resolve(100)
    return Promise.resolve(200)
}

const res1 = fn1() //执行 async 函数,返回的是一个 Promise 对象
res1.then(data => {
    console.log('data',data) //200
})


!(async function () {
    const p1 = Promise.resolve(300)
    const data = await p1 // await 相当于 Promise then
    console.log('data',data) //300
})()

!(async function () {
    const data1 = await 400 // await 相当于 Promise.resolve(400)
    console.log('data1',data1)
})()
  • await 后面跟Promise对象,如果是数字可以转换成 Promise.resolve(数字)的形式(自动的,不需要我们操作),因为,async 函数返回的是 Promise 对象,也可以将此类函数放在 await 之后。

;(async function () {
    const p4 = Promise.reject('err1') // rejected状态
    try {
        const res = await p4
        console.log(res)
    } catch (ex) {
        console.error(ex) // try...catch 相当于 promise catch
    }
})()

特殊:

;(async function () {
    const p4 = Promise.reject('err1') //rejected状态
    const res = await p4 //这里等同于 Promise.reject('err1').then()
    console.log(res) //没结果的,resolved状态 才能触发 then 
})

3.3 异步的本质

  • async/await 是消灭异步回调的终极武器
  • JS 还是单线程,还得是有异步,还得是基于 event loop
  • async/await 只是一个语法糖
  • await 后面都可以看做是 callback 里的内容,执行时需要按照异步顺序执行。
async function async1 () {
   console.log('async1 start') // 2
   await async2() //async2 return 的是一个undefined
   // await 后面都可以看做是 callback 里的内容,即异步。后面的内容相当于计时器里的内容,基于event loop
   console.log('async1 end')// 5
}

async function async2() {
   console.log('async2')//3 
}

console.log('script start') //1
async1()
console.log('script end') // 4

4 微任务/宏任务

宏任务(macroTask) 和 微任务(micro Task)

宏任务:setTimeout,setInterval, Ajax, DOM事件(不是异步)

微任务:Promise async/await

微任务执行时机比宏任务要早!!!

console.log(100)
//宏任务
setTimeout(() => {
    console.log(200)
})
setTimeout(() => {
    console.log(201)
})
// 如果有两个宏任务,按照进入Callback queue 的顺序,先进先出(在定时相同的情况下)
// 微任务
Promise.resolve().then(() => {
    console.log(300)
})
console.log(400)
// 100 400 300 200 201

4.1 event loop 和 DOM 渲染

在代码执行过程中有DOM操作时,event loop的触发前提:1.call stack 空闲;2.DOM 渲染完毕;3.触发 event loop,轮询 callback queue 队列

  1. 每次 Call Stack 清空(即每次轮询结束),即同步任务执行完。
  2. 都是 DOM 重新渲染的机会,DOM 结构如有改变则重新渲染。
  3. 然后再次触发下一次的 Event loop
const $p1 = $('<p>1234</p>')
const $p2 = $('<p>1234</p>')
const $p3 = $('<p>1234</p>')
$('#container')
	.append($p1)
	.append($p2)
	.append($p3)
console.log('length',$('#container').children().length) //3
alert('本次 call stack 结束,DOM 结构已更新,但尚未触发渲染')
// alert 会阻断 js  执行,也会阻断 DOM 渲染,便于查看效果
  • 宏任务: DOM 渲染后触发,如 setTimeout
  • 微任务: DOM渲染前触发, 如Promise
// 微任务:DOM 渲染后出发,如 setTimeout
Promise.resolve().then(() => {
    console.log('length1',$('#container').children().length) 
    alert('Promise then') // DOM 还没有渲染
})
 // 宏任务: DOM 渲染后触发
setTimeout(() => {
    console.log('length2', $('#container').children().length)
    alert('setTimeout') //DOM  已经渲染了
})

4.2 微任务和宏任务的根本区别

微任务是 ES6语法规定的(存放在微任务队列中),先于DOM渲染。宏任务是由浏览器规定的 。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值