2024年Web前端最新随笔-async await的基本用法以及使用陷阱,高效使用技巧(5),字节跳动算法工程师面试经验

ES6

  • 列举常用的ES6特性:

  • 箭头函数需要注意哪些地方?

  • let、const、var

  • 拓展:var方式定义的变量有什么样的bug?

  • Set数据结构

  • 拓展:数组去重的方法

  • 箭头函数this的指向。

  • 手写ES6 class继承。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

微信小程序

  • 简单描述一下微信小程序的相关文件类型?

  • 你是怎么封装微信小程序的数据请求?

  • 有哪些参数传值的方法?

  • 你使用过哪些方法,来提高微信小程序的应用速度?

  • 小程序和原生App哪个好?

  • 简述微信小程序原理?

  • 分析微信小程序的优劣势

  • 怎么解决小程序的异步请求问题?

await 修饰一个promise对象,相当于调用了promise对象的then方法获得了成功结果,并作为await的返回值

如果await修饰的promise对象状态变为失败,则await会抛出异常,需要try…catch捕获

await也可以用于修饰非promise对象,但是底层会将其封装为promise对象后再获取其成功结果作为await的返回值

相当于await直接将修饰的非promise值,直接作为返回值

await会等待promise对象的成功结果,在promise对象成功结果未返回期间,await会暂停async函数代码向下执行

注意这里虽然await修饰的是一个非promise对象 1,但是底层会将 1 封装为promise对象,然后执行它的then方法,将成功结果 1 返回给await,所以这里await 1 会产生一个微任务,微任务会等待所有同步代码执行完成后执行。

需要注意的是async函数中的console.log(1)被阻塞执行的原因:

async函数的主体实现是(生成器函数generator),调用实现是(执行器函数co)

而generator执行无法像普通函数一样被调用执行,generator调用只会返回一个迭代器对象iterator。

以下是co函数的简要逻辑:

而iterator可以控制generator函数的执行,当iterator.next()调用时,generator函数就会执行函数体,当执行遇到yield(相当于async函数中await)[await 1],执行暂停,并且yield会产出一个值[1](相当于async函数中await修饰的值)作为iterator.next()的返回值,而co函数会拿到这个返回值,将其包装为一个promise对象[Promise.resolve(1)],并调用其then方法获取成功结果[此时产生微任务],等得到成功结果后,将其作为下一次iterator.next(data)的参数data[async函数的后续代码 console.log(1)执行依赖于这里的iterator.next,但是这里的iterator.next又需要等带微任务的成功结果],该data最终会传递给上一次next对应的yield的返回值,然后再次继续执行generator后续代码,直到又遇到yield,重复以上逻辑,或者遇到return,结束generator执行。

使用陷阱


对于可以并行的异步任务错误使用await,导致执行效率下降

我们直到async await可以优雅地实现异步任务间串行,但是我们需要知道异步任务串行地场景,一般是后一个异步任务依赖于上一个异步任务地结果,此时才需要串行异步任务。

但是多个互相无依赖关系地异步任务是不需要串行的,强行使用串行反而会降低执行效率

可以发现a,b,c三个读取文件的无依赖的异步任务,每一个异步任务读取大约2s多一点,这里由于使用了await串行,所以导致a读取完毕后,才能读取b,b读取完毕后才能读取c,将效率降低了3倍数。

那么await如何实现并行呢?

怎么说呢,其实await无法实现并行,每个await都会开启一个微任务,并阻塞async函数中后续代码执行

那么在异步任务并行场景下,就不得不少用await了吗?

这种写法为什么也能实现await并行呢?

我们知道 readFilePromisify(‘a.txt’)会返回一个promise对象,且在返回promise对象的同时就已经开启了异步任务执行,

对比下面这种方式

const a = await readFilePromisify(‘a.txt’)

const b = await readFilePromisify(‘b.txt’)

const c = await readFilePromisify(‘c.txt’)

由于await会阻塞后续代码执行,所以当在读取a.txt的过程中,后面的b,c的readFilePromisify是没有被调用的,也就无法开启异步任务

forEach的回调函数中使用await等待异步操作完成,实际forEach不会暂停遍历操作

我们来重写一些Array.prototype.forEach

可能还是不够明白,我们将arr.forEach和Array.prototype.forEach合并

我们知道await只能阻塞async函数向下执行,并且await会开启一个微任务,当开启微任务后,JS引擎线程就会将微任务加入异步任务队列,然后继续执行同步代码,即新一轮的for循环

所以需要注意:await只会阻塞async函数内部代码执行,而不会阻塞async函数外部同步代码的执行

那么如果我们想要让遍历等待遍历中的异步任务完成后才继续下一次遍历该如何实现呢?

 测试发现上面,for…in,for…of循环都可以

那么是否意味着forEach,只要也在外面包装一个async函数壳子就行了呢?

答案是不行,这里其实是第三个坑,上面代码貌似await是定义在async函数中,但是再仔细检查一下,就会发现await是定义在了arr.forEach的回调函数中,而arr.forEach函数不是一个async函数

即要求await必须在async函数的执行上下文中使用

另外还有一种方式可以实现循环遍历过程中等待异步操作的方式

最后

在面试前我花了三个月时间刷了很多大厂面试题,最近做了一个整理并分类,主要内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。

包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。

  • HTML5新特性,语义化

  • 浏览器的标准模式和怪异模式

  • xhtml和html的区别

  • 使用data-的好处

  • meta标签

  • canvas

  • HTML废弃的标签

  • IE6 bug,和一些定位写法

  • css js放置位置和原因

  • 什么是渐进式渲染

  • html模板语言

  • meta viewport原理

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值