前言
根据慕课网的《快速搞定前端技术一面 匹配大厂面试要求》课程所整理的题目,陆续更新
常考面试题
如果你觉得自己对异步该部分知识掌握得已经差不多了,那么你可以不看前面的,直接跳转到最底下查看面试场景题
1.同步、异步的区别是什么?
异步:不会阻塞代码的执行
同步:会阻塞代码执行
同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。
- 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。
2.手写promise
function myPromise(fn) {
// 回调函数集合
this.callbacks = []
// resolve 方法
// 往实例上挂载 data
// 依次执行回调函数
function resolve(value) {
setTimeout(() => {
this.data = value;
this.callbacks.forEach((callback) => callback(value));
});
}
// 将 resolve 方法交还
fn(resolve.bind(this));
}
myPromise.prototype.then = function (onResolve) {
return new myPromise((resolve) => {
this.callbacks.push(() => {
// 将结果返回给传入的回调
const res = onResolve(this.data);
// 链式调用 的返回值还是 Promise 对象时
if (res instanceof myPromise) {
res.then(resolve);
} else {
resolve(res);
}
});
});
};
3.手写用promise加载一张图片
function loadImg(src) {
const p = new Promise(
( resolve , reject ) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error(`图片加载失败${src}`))
}
img.src = src
}
)
return p
}
const url = 'xxx'
loadImg(url).then( img => {
console.log(img.width)
return img
} ).then( img => {
console.log(img.height)
} ).catch( ex => console.error(ex) )
4.前端使用异步的场景有哪些?
- ①网络请求,ajax图片加载
- ②定时任务,setTimeout
5.异步产生的背景及异步机制
异步产生的背景
- ①JS是单线程语言,只能同时做一件事
- ②没有异步之前,遇到等待(网络请求、定时任务)会卡住
- ③同步会阻塞代码执行
异步机制
- 采用回调callback函数形式(但是这样子会产生坏代码,回调表达异步流程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度很大。难于理解的代码是坏代码,会导致坏 bug,也就会产生大家一般所说的回调地狱callback hell问题)
6.promise产生背景及作用
回调地狱(异步调用异步)callback hell,于是产生了promise
- 作用:promise把url变成了非嵌套的模式,转化成了管道形式,变成管道传递,如果是直接用ajax请求,如果有一份文件很大,那么页面就会卡住一直等待那一份数据请求成功
- event loop 是异步回调的实现原理
7.请描述event loop的机制
event是异步回调的实现原理
- ①同步代码,一行一行放在call stack中执行
- ②遇到异步,会先
记录
下,等待时机 - ③时机到了,就移动到callback queue
- ④如果call satck为空时(同步代码执行完),空闲下来之后,先执行微任务,尝试DOM渲染
- ⑤event loop开始工作,执行宏任务
- ⑥轮询查找callback queue,如有任务则移动到call stack执行
- ⑦继续轮询查找
8.什么是宏任务、微任务?两者有什么区别?
宏任务:DOM渲染后触发,如setTimeout
- script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering、Ajax、DOM事件
微任务:DOM渲染前触发,如Promise
- promise、Object observe、Mutation Observe、async/await
任务优先级:process.nextTick > promise.then > setTimeout > setImmediate
如何理解宏任务跟微任务处理的关系呢?
- 记得之前看过一个例子是这么描述的,比较好理解,就像那些去银行排队办理业务的人,柜员里的业务员是js单线程,来排队的每一个人是宏任务,每一个人会办理几个业务,每一个业务是微任务,也就是每一个宏任务带着几个微任务一个个去执行。也就是只能处理完了当前所有的微任务,才会开始下一个宏任务
9.promise有哪三种状态?如何变化
三种状态
- pending 过程中
- fulfilled 解决了(成功)
- rejected 被拒绝(失败)
- 一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)(面试场景题中会在这埋坑)
如何变化?
- then正常会返回fulfilled
- catch正常也会返回fulfilled
- 有报错就返回rejected
- resolved触发后续的then
- rejected触发后续的catch
10.async/await简单认识
- Promise的then、catch链式调用,其实也还是基于回调函数
- 而async/await是同步语法,彻底消灭回调函数
- async/await和promise并不互斥,是相辅相成的作用
- 执行async函数,返回的是Promise对象,await相当于promise的then,try、catch可捕捉异常,代替了promise的catch
- async/await只是一个语法糖,本质上JS还是单线程,还是异步,还是基于event loop,但这颗糖真香
11.各种面试异步场景题
下面是各种相关异步场景题,可以做一下,然后左滑查看注释里的答案
1.setTimeout
console.log(1)
setTimeout( function () {
console.log(2)
} , 1000 )
console.log(3)
setTimeout( function () {
console.log(4)
} , 0 )
console.log(5)
// 1 3 5 4 2
2.promise状态(1)
Promise.resolve()
.then( () => {
console.log(1)
throw new Error('error1') // 排出错误,状态变为rejected
} ).catch( () => { // rejected状态触发catch
console.log(2)
} ).then( () => { // catch返回fulfilled状态,再触发then
console.log(3)
} )
// 1 2 3
3.promise状态(2)
Promise.resolve()
.then( () => {
console.log(1)
throw new Error('error1')
} ).catch( () => {
console.log(2)
} ).catch( () => {
console.log(3)
} )
// 1 2
4.promise状态(3)
Promise.resolve()
.then( () => {
console.log(1)
} ).catch( () => { // fulfilled状态不触发catch
console.log(2)
} ).then( () => {
console.log(3)
} )
// 1 3
5.微任务、宏任务
console.log(100)
setTimeout( () => {
console.log(200)
} ) // 宏任务
Promise.resolve().then( () => {
console.log(300)
} ) // 微任务
console.log(400)
// 100 400 300 200
// 微任务执行时机比宏任务早
如果以上题目你都做对了,那么你对promise的理解已经差不多了,那么接下来看看async/await
6.async/await (1)
(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)
console.log('c:',c)
console.log('end')
})()
// start
// a 100
// b 200
// 报错
7.async/await (2)
async function async1() {
console.log('async1 start')
await async2()
// await 的后面,都可以看作是 callback 里的内容,即异步
console.log('async1 end') // 异步内容先不执行,放到一边,先执行完同步内容再回来执行异步 5
}
async function async2() {
console.log('async2')
}
console.log('script start')
async1()
console.log('script end')
// script start、async1 start、async2、script end、async1 end
8.async/await (3)
async function async1() {
console.log('async1 start')
await async2()
// 下面都是异步回调
console.log('async1 end')
await async3()
console.log('async1 end 2')
}
async function async2() {
console.log('async2')
}
async function async3() {
console.log('async3')
}
console.log('script start')
async1()
console.log('script end')
// script start、async1 start、async2、script end、async1 end、async3、async1 end 2、
9.终极结合场景面试题(听说是当年今日头条面试原题)
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout( function () {
console.log('setTimeout')
} , 0 )
async1()
new Promise ( function (resolve) {
console.log('promise1')
resolve()
} ).then( function () {
console.log('promise2')
} )
console.log('script end')
//script start、async1 start、async2、promise1、script end、async1 end、promise2、setTimeout、
如果这道题你也做对了,那说明你对这异步这方面的知识已经掌握得差不多了
写在文末
如果你觉得我写得还不错的话,可以给我点个赞哦^^,如果哪里写错了、写得不好的地方,也请大家评论指出,以供我纠正。
其它文章
- 前端一面基础知识 ②——作用域和闭包(面试场景题)
- 前端一面基础知识 ①——CSS面试题
- 数据结构之二叉树的遍历
- 仿去哪儿网项目视频学习总结
- Webpack简单学习
- 面筋系列①——滴滴sp一面面试真题
- 面筋系列②——滴滴实习生一面凉经
本文使用 mdnice 排版