1、Event Loop(事件循环/事件轮询)
- 同步代码一行一行地放置Call Stack(调用栈)执行
- 遇到异步时,会扔掉Web APIs执行,等待机会
- 当时机成熟的时候,回调会转移到Callback Queue(回调队列)
- 如果Call Stack空表示同步代码执行,然后Event Loop开始循环
- 轮询查找Callback Queue,假如有,把它放进去Call Stack里面执行
注意:js异步(定时器,ajax)基于使用回调event loop的;DOM基于事件(点击,鼠标滑过)使用回调event loop实现的
2、Promise有三种状态
- pending,刚创建Promise没有调用resolve或者reject,属于等待定状态
- fulfilled,调用了resolve之后,状态会变成fulfilled,属于完成状态
- rejected,调用reject以后,状态会变成rejected,属于拒绝状态
只能有注意状态pending->fulfilled或者pending->rejected,不可逆转
3、Promise里面的then和catch如何影响状态的变化
- then正常返回fulfilled,有错误的返回rejected
- catch正常返回fulfilled,有错误的返回rejected
let a = Promise.resolve(123).then(res=>{
console.log(res) }) console.log(a)//fulfilled let b = Promise.resolve(123).then(res=>{
throw new Error('bad') }) console.log(b)//rejected let c = Promise.reject('bad').catch(err=>{
console.log(err) }) console.log(c)//fulfilled let d = Promise.reject('bad').catch(err=>span class="token punctuation">{
throw new Error('BAD')
console.log(err)
})
console.log(d)//rejected
4、then和catch的链式调用例题
//题目1
Promise.resolve().then(()=>{
console.log(1)//fulfilled
}).catch(()=>{
console.log(2)
}).then(()=>{
console.log(3)//fulfilled
})//1 3
//题目2
Promise.resolve().then(()=>{
console.log(1)
throw new Error('k')//rejected
}).catch(()=>{
console.log(2)//fulfilled
}).then(()=>{
console.log(3)
})//1 2 3
//题目3
Promise.resolve().then(()=>{
console.log(1)
throw new Error('k')//rejected
}).catch(()=>{
console.log(2)//fulfilled
}).catch(()=>{
console.log(3)
})//1 2
5、async-await语法
- async要和await配合使用
- async-await函数其实是通过同步的写法来执行异步
- 立即执行函数,要在前面加一个感叹号,否则就会和前面的连接在一起,识别为非法函数
- await后面可以接Promise函数,也可以接async函数
function LoadImg(src){
return new Promise((resolve,reject)=>{
const img = document.createElement('img')
img.src = src
img.onload = function(){
resolve(img)
}
img.reject = function(){
reject('加载错误')
}
})
}
const img1 = 'https://wwc.alicdn.com/avatar/getAvatar.do?userNick=tb376421096&width=60&height=60&type=sns&_input_charset=UTF-8'
const img2 = 'https://gw.alicdn.com/bao/uploaded/i1/15388591/O1CN0105eOif2DKjHQ7d65U_!!15388591.jpg_300x300q90.jpg_.webp'
async function getimg1(){
let res = await LoadImg(img2)
return res
}
//立即执行函数,要在前面加一个感叹号,否则就会和前面的连接在一起,识别为非法函数
!(async function(){
//await后面可以接Promise函数
let res1 = await LoadImg(img1);
console.log(res1)
//await后面也可以接async函数
let res2 = await getimg1()
console.log(res2)
})()
6、async-await和Promise的关系
执行async函数,返回的是Promise对象 await相当于Promise的then try…catch可以捕获异常,相当于Promise的catch
//执行顺序,先是同步的两个输出,然后是立即执行函数,在是fn1的异步
async function fn(){
return 100//asycn会吧200封装成Promise返回
}
let a = fn().then(r=>{
console.log(r)})
console.log(a)
async function fn1(){
return Promise.resolve(200)
}
let b = fn1().then(r=>{
console.log(r)})
console.log(b)
!(async function fn2(){
// let res = await 300//这里会封装成Promise
let res = await Promise.resolve(300)
console.log(res)
})()
!(async function fn3(){
//try...catch相当于Promise里面的catch
try{
let res = await Promise.reject("400")
console.log(res)
}catch(err){
console.log(err)
}
})()
7、异步的本质
asycn-await的本质还是回调函数,js还是单线程的语言
async function fn1(){
console.log(2)
await fn2()//执行到这里先执行普通函数的内容
//await下面的都是属于回调的内容
console.log(5)
await fn3()//执行到这里先执行普通函数的内容
//await下面的都是属于回调的内容
console.log(8)
}
async function fn2(){
console.log(3)
}
async function fn3(){
await fn4()
//await下面的都是属于回调的内容
console.log(7)
}
async function fn4(){
let res = await 6
console.log(res)
}
console.log(1)
fn1()
console.log(4)
//1 2 3 4 5 6 7 8
8、for…of的使用场景(遍历异步)
for… of可以解决forEach是同步输出的问题 for…in输出的是键名,for…of输出的是键值 for…in会遍历原型链,性能差,不推荐,for…of只会便利当前对象
function fn1(num){
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(num+=1)
}, 1000);
})
}
let a = [1,2,3]
a.forEach(async (item)=>{
//这种情况下因为forEach是同步执行的,所以会很快吧所有结果计算出来,然后同时输出
let res = await fn1(item)
console.log(res)
})
let b = [4,5,6]
!(async function(){
try{
for(let i of a){
//用来for...of 后,会一秒输出一个执行
let res = await fn1(i)
console.log(res)
}
}catch(e){
console.log(e)
}
})()
9、宏任务与微任务
宏任务:setTimeout、setInterval、Ajax,Dom事件(不是异步但是是基于Event Loop) 微任务:Promise、async/await 微任务比宏任务先执行
console.log(1)
//宏任务
setTimeout(()=>{
console.log(4)
})
//微任务
Promise.resolve(3).then(e=>{
console.log(e)
})
console.log(2)
10、Event Loop和Dom渲染的联系
- 首先执行同步代码,见一个把他放在Call Stack里面执行
- 遇到异步的代码,把它放在Web APIs里面处理,然后继续执行同步代码
- 当执行完同步代码执行完毕,即Call Stack为空时,这时先尝试Dom渲染
- 如果有就先渲染,如果没有就开启Event Loop,从Callback Queue里面执行Web APIs传进来的回调
11、宏任务和微任务的区别
宏任务:DOM渲染后触发 微任务:DOM渲染前触发
12、 宏任务和微任务的根本区别
- 首先执行同步代码,见一个把他放在Call Stack里面执行
- 遇到异步的代码,把它放在Web APIs里面处理,然后继续执行同步代码,在遇到Promise或者async时,会把它放在Micor Task Queue里面
- 当执行完同步代码执行完毕,即Call Stack为空时,这时先去执行微任务队列里面的东西
- 然后接着尝试Dom渲染,如果有就先渲染
- 如果没有就开启Event Loop,从Callback Queue里面执行Web APIs传进来的回调
13、async-await的面试题
//例1
async function fn(){
return 100
}
!(async function(){
let a = fn()
let b = await fn()
console.log(a)//Promise对象
console.log(b)//100
})()
//例题2
!(async function fn1(){
console.log(1)
const a = await 2
console.log(a)
const b = await Promise.resolve(3)
console.log(b)
const c = await Promise.reject(4)
console.log(c)
console.log(5)
})()
//1 2 3 报错(因为await不能处理reject)
14、场景题-外加async- await的顺序问题
async function async1 (){
console.log(1) //2
await async2()
//微任务
console.log(2)//6
}
async function async2(){
console.log(3) //3
}
console.log(4) //1
setTimeout(function (){
//宏任务
console.log(5) //8
},0)
async1()
new Promise(function(resolve,reject){
console.log(6) //4
resolve()
}).then(function(){
//微任务
console.log(7) //7
})
console.log(8) //5
// 4 1 3 6 8 2 7 5
//首先执行同步的代码,
//遇到宏任务放在web apis里面,遇到微任务放在micro task queue里面
//完成同步代码,即call stack为空后
//执行微任务(Promise和async)
//渲染DOM
//开始Event Loop,按顺序执行callback queue里面的内容
15、补充Promise.all([a,b,c])和Promise.race([a,b,c])
Promise.all()是等前面的都fulfilled以后,返回一个新的Promise,包含所有的结果 Promise.race()只要有一个为fulfilled,就返回