单线程和异步
JS是单线程语言,只能同时做一件事;浏览器和nodejs已支持JS启动进程,如Web Worker;JS和DOM渲染共用同一个线程,因为JS可修改DOM结构。
因此遇到等待(网络请求,定时任务)不能卡住,因此需要异步,异步是基于callback回调函数形式来调用
异步和同步
基于JS是单线程语言,异步不会阻塞代码的执行,而同步会阻塞代码的执行
应用场景
网络请求,如ajax图片加载;定时任务,如setTimeout
//图片加载
console.log('start')
let img = document.createElement('img')
img.onload = function() {
console.logg('loaded')
}
img.src = '/xxx.png'
console.log('end')
callback hell(回调地狱)
Promise
初始化promise时(new Promise),传入的函数会立刻被执行
异步应用场景
网络请求,如ajax图片加载;定时任务,如setTimeout
event loop(事件循环/事件轮询)
由于JS是单线程运行的,异步要基于回调来实现,event loop就是异步回调的实现原理。异步(setTimeout,ajax等)使用回调,基于event loop,DOM事件也使用回调,基于event loop(但DOM事件不属于异步)
event loop过程:同步代码,一行一行放在Call Stack执行,直接执行;遇到异步,会先“记录”下,等待时机(定时、网络请求和事件触发等);时机到了,就移动到Callback Queue;如Call Stack为空(即同步代码执行完)Event Loop开始工作;轮询查找Callback Queue,如有则移动到Call Stack执行,然后继续轮询查找。(当Call Stack空闲,会尝试DOM渲染)
Promise
状态
有三种状态:pending(过程中) resolved(已解决) rejected(失败),只能从pending——>resolved/rejected,并且变化是不可逆的。
状态的表现
pending状态,不会触发then和catch;resolved状态,会触发后续的then回调函数;rejected状态,会触发后续的catch回调函数。
then和catch改变状态
then正常返回resolved,里面有报错则返回rejected;catch正常返回resolved,里面有报错则返回rejected。rejected触发catch回调,resolved触发then回调
async/await
通过同步的写法写异步的作用。还是解决异步回调callback hell的问题,虽然Promise then catch是链式调用,但也是基于回调函数。async/await是同步语法,可以彻底消灭回调函数。
async/await和Promise的关系
async/await是消灭异步回调的终极武器,但和Promise并不互斥,反而两者相辅相成。
执行async函数,返回的是Promise对象,await相当于Promise的then,try...catch可捕获异常,代替了Promise的catch。async是封装的Promise
async function fn1() {
//return 100 //async自动讲这个封装成一个Promise对象,相当于return Promise.resolve(100)
return Promise.resolve(200)
}
const res1 = fn1() //执行async函数,返回的是一个Promise对象
console.log('res1',res1) //promise对象
res1.then(data => {
console.log('data',data) //100
})
---------------------------------------
!(async function () {
const p1 = Promise.resolve(300)
const data = await p1 // await相当于Promise then
console.log('data',data)
})()
----
!(async function () {
const data1 = await 400 // await Promise.resolve(400)
console.log('data1',data1)
})()
----
!(async function () {
const data2 = await fn1() // await Promise.resolve(400)
console.log('data2',data2)
})()
----
!(async function () {
const p4= Promise.reject('err1') // await Promise.resolve(400)//reject状态
try {
const res = await p4
console.log(res)
} catch (ex) {
console.error(ex) // try...catch相当于promise catch
}
})()// 注意await不能直接接reject
异步的本质
async function async(){
console.log('async1 start') // 2
await async2() //undefined
//await的后面都可以看作是callback里的内容,即异步
//类似,event loop,setTimeout()
//setTimeout((function(){console.log('async1 end')})
//Promise.resolve().then(() => {console.log('async1 end')}
console.log('async1 end')//5
}
async function async2(){
console.log('async2') //3
}
console.log('script start') //1
async1() //与定时器等不同,这是函数调用,就直接回到函数定义的地方去执行了
console.log('script enf')//4
//同步代码已经执行完(event loop)
//结果为script start async1 start async2 script enf async1 end
for...of
for...in(以及forEach for)是常规的同步遍历;for...of常用于异步的遍历
宏任务macroTask和微任务microTask
宏任务:setTimeout,setInterval,Ajax,DOM事件;微任务:Promise,async/await;微任务执行时机比宏任务要早。
event loop和DOM渲染
JS单线程,而且和DOM渲染共用一个线程;JS执行的时候,得留一些时机供DOM渲染
每次Call Stack清空(即每次轮询结束),即同步任务执行完都是DOM重新渲染的机会,DOM结构如有改变则重新渲染,然后再去触发下一次Event loop
微任务和宏任务的区别
宏任务:DOM渲染后触发,如setTimeout;微任务:DOM渲染前触发,如Promise
微任务是ES6语法规定的,宏任务是由浏览器规定的。宏任务会放到Web APIs中而微任务不会