JS部分面试题必备
1. 异步的面试题
问答题
- 请描述event loop (事件循环/事件轮询)的机制,可画图
- 什么是宏任务和微任务,两者有什么区别?
- Promise有哪三种状态?如何变化?
- 场景题-promise then 和catch 的链接
// 第一题
Promise.resolve().then(() => {
//返回resolved状态的promise后会执行.then的回调,.then的回调又会返回resolved状态的promise,resolved状态的promise不会执行catch,它会执行.then的回调。整个最后返回resolved状态的promise
console.log(1)
}).catch(() => {
console.log(2)
}).then(() => {
console.log(3)
}) //resolved
// 1 3
// 第二题
Promise.resolve().then(() => {
//.then里报错,那么.then返回的是rejected状态的promise,rejected的promise触发.catch回调,因为.catch没有报错,所以返回的是resolved状态的promise,resolved状态的promise会触发.then。整个最后返回的还是resolved状态的promise
console.log(1)
throw new Error('erro1')
}).catch(() => {
console.log(2)
}).then(() => {
console.log(3)
}) //resolved
// 1 2 3
// 第三题
Promise.resolve().then(() => {//返回rejected状态的promise 触发 catch回调
console.log(1)
throw new Error('erro1')
}).catch(() => { //返回resolved状态的promise 触发 then回调
console.log(2)
}).catch(() => { // 注意这里是 catch
console.log(3)
})
// 1 2
Promise.reject().then(()=>{
console.log(1);
throw new Error('error1');
}).then(()=>{
console.log(2);
}).catch(()=>{
console.log(3);
})
//3
Promise.reject().catch(()=>{
console.log(1);
throw new Error('error1');
}).then(()=>{
console.log(2);
}).catch(()=>{
console.log(3);
})
//1,3
Promise.reject().catch(()=>{
console.log(1);
// throw new Error('error1');
}).then(()=>{
console.log(2);
}).catch(()=>{
console.log(3);
})
//1,2
Promise.reject().catch(()=>{
console.log(1);
}).then(()=>{
console.log(2);
throw new Error('error1');
}).catch(()=>{
console.log(3);
})
//1,2,3
Promise.resolve().catch(() => {
console.log(1);
}).then(() => {
console.log(2);
throw new Error('error1');
}).catch(() => {
throw new Error('error2');
console.log(3);
})
//2
-
场景提 - async/await 语法
(async function(){ console.log('start'); const a = await 100; console.log('a',a); const b = await Promise.resolve(200); console.log('b',b); const c = await Promise.reject(300); //reject状态的Promise,不会执行await要用try...catch捕捉异常错误,此题报错显示没有捕获的错误,所以后边的代码不会执行 console.log("c",c); console.log('end'); })() //start //a 100 //b 200 //Uncaught (in promise) 300
async function fn(){ return 100; } (async function(){ const a = fn(); console.log(a); //调用async修饰的函数返回的是promise对象 且这里的值是100 promise{<fulfilled>:100} const b = await fn(); console.log(b);//await相当于then 所以直接返回数值100 })()
-
场景提 - promise 和setTimeout的顺序
console.log(100); setTimeout(()=>{ console.log(200); }) Promise.resolve().then(()=>{ console.log(300); }) console.log(400);
-
场景题 - 外加 async/await 的顺序问题
//网上经典面试题 async function async1 () { console.log('async1 start') //2 //await后面都是回调内容--微任务 await async2() // 这一句会同步执行,返回 Promise ,其中的 `console.log('async2')` 也会同步执行 console.log('async1 end') //6 上面有 await ,下面就变成了“异步”,类似 cakkback 的功能(微任务) } async function async2 () { console.log('async2')//3 } console.log('script start')//1 setTimeout(function () { // 异步,宏任务 console.log('setTimeout')//8 }, 0) async1() //初始化promise时,传入的函数会立刻被执行 new Promise (function (resolve) { // 返回 Promise 之后,即同步执行完成,then 是异步代码 console.log('promise1') //4 Promise 的函数体会立刻执行 resolve() }).then (function () { // 异步,微任务 console.log('promise2')//7 }) console.log('script end')//5 // 同步代码执行完之后,屡一下现有的异步未执行的,按照顺序 // 1. async1 函数中 await 后面的内容 —— 微任务 // 2. setTimeout —— 宏任务 // 3. then —— 微任务 //除了宏任务和微任务其他都是同步的
event loop
- JS是单线程运行的
- 异步要基于回调来实现
- event loop 就是异步回调的实现原理
JS 如何执行?
-
从前到后,一行一行执行
-
如果某一行报错,则停止下面代码的执行
-
先把同步代码执行完,再执行异步
-
总结event loop过程
第一步:把
console.log('Hi')
这行代码推入调用栈调用栈执行代码,执行完之后打印出Hi
第二步:这行代码执行完毕,清空调用栈第三步:执行到setTimeout()函数(浏览器定义(WebAPIs)的),把异步函数cb1放到定时器里面,定时是5s,5s之后把cb1放到Call Queue里面,至此setTimeout函数(三行代码)结束,console里不会打印任何东
![image-20210405134825179](/Users/yangxianqiang/Library/Application Support/typora-user-images/image-20210405134825179.png)第四步: 执行最后一行代码
console.log("Bye")
,和第一行一样,把它推送到调用栈中执行打印Bye
执行完毕之后,调用栈清空。此时后边已经没有要在被推送到callstack中执行的代码了,也就是同步代码已经执行完毕了,这时就会启动EventLoop(浏览器内核自动启动),他会像永动机或者死循环一样不断的执行循环,它会在callback queue(异步回调函数)里去找函数,如果有的话它会把函数放到callStack里执行,cb1有函数体,把
console.log('cb1')
放到调用栈里执行, 最后清空调用栈-
Call Stack:真正触发执行某行代码
-
Web APIs:对于浏览器的运行环境和一些API的定义,setTimeout(setInterval,dom操作)不是SE里定义的,而是浏览器里面定义的API.处理定时或者异步的api的
-
Event Loop:当Call Stack 空了的时候(同步代码执行完毕,尝试dom渲染后)开始触发,它就像永动机一样或者想死循环一样不停地在callback Queue中找有没有可以执行的函数,如果有的话就把它移动到Call Stack中执行,
-
Callback Queue:回调函数的队列
-
-
同步代码,一行一行放在call stack执行
-
遇到异步,会先’‘记录’’下,等待时机(定时,网络请求等)
-
时机到了,就移动到Callback Queue
-
如果Call Stack 为空,尝试渲染dom之后,(即同步代码执行完)Event Loop开始工作
-
轮询查找Callback Queue中的回调函数,如有则移动到CallStack执行,再尝试渲染dom
-
然后继续轮询查找(像永动机一样)
-
Callstack,尝试dom渲染,触发下一次eventloop 轮询查找是循环往复的