JS单线程和异步
- JS是单线程语言,只能做一件事
- 浏览器和nodejs已支持JS启动进程,如web worker
- JS和DOM渲染共用同一线程,因为JS可以修改DOM结构
- 遇到等待(网络请求,定时任务)不能卡住,所以需要异步
- 异步是以callback函数形式
- promise解决了callback hell
JS同步与异步
基于JS是单线程语言,异步不会阻塞代码执行,同步会阻塞代码执行
JS异步应用场景
- 网络请求:ajax图片加载
- 定时任务:setTimeout
手写一个promise加载图片
function loadImg(src) {
const p = new Promise(
(resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const error = new Error('图片加载失败')
reject(error)
}
img.src = src
}
)
return p
}
const url = "https://juejin.cn/user/2"
const url2 = "https://juejin.cn/1"
loadImg(url).then(img => {
console.log(img.width);
return img //普通对象
}).then(img => {
console.log(img.height);
return loadImg(url2) //promise 实例
}).then(img2 => {
console.log(img2.width);
return loadImg(url2)
}).catch(ex => {
console.log(ex);
})
JS执行机制
- 从前往后,一行一行执行
- 如果某一行执行错误,则停止下面代码的执行
- 先把同步代码执行完,再执行异步
event loop(事件循环/事件轮询)
异步是基于callback来实现,event loop是异步回调的实现原理
过程:
- 同步代码,一行一行放在call stack执行
- 遇到异步会先"记录"下,等待时机(定时、网络请求等)
- 时机一到就放在callback queue
- 如遇call stack为空(即同步代码执行完),event loop开始工作
- 轮询查找callback queue,如有则移动到call stack执行
- 然后继续轮询查找(永动机一样)
Promise
三种状态:
- Pending(进行中,初始状态,既不是成功,也不是失败状态)
- Resolved(已完成,又称 Fulfilled)
- Rejected(已失败)
这三种状态的变化途径只有2种:
- 异步操作从 未完成 pending => 已完成 resolved
- 异步操作从 未完成 pending => 失败 rejected
- 状态一旦改变,就无法再次改变状态
then和catch改变状态 then正常返回resolved,里面有报错则返回 rejected catch正常返回resolved,里面有报错则返回 rejected
async/await
为了解决异步回调callback hell,Promise then catch链式调用,但也是基于回调函数,async/await是同步语法,彻底消灭回调函数
async/await和Promise的关系
async/await是消灭异步回调的终极武器,但和Promise并不互斥,反而相辅相成
总结: 异步本质
- async/await是消灭异步回调的终极武器
- JS是单线程语言,还是得有异步,还是得基于event loop
- async/await本质只是一个语法糖
for...of常用于异步遍历
宏任务macroTask和微任务microTask
- 宏任务macroTask:setTimeout,setInterval,Ajax,DOM事件
- 微任务microTask:Promise,async/await
- 微任务执行时机比宏任务要早,微任务是在DOM渲染前触发,宏任务是在DOM渲染后触发
event loop和DOM渲染
- 每次call stack清空空(即同步代码执行完)
- 都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
- 然后再次去触发event loop