前端面试之问到promise怎么办?

2023-05-16

前言

Promise作为面试中的经典考题,我们一定要深刻学习和理解它! Promise有什么用呢?答:我们拿它解决异步回调问题。Pomise是ES6里面新增的一种异步编程的解决方案。现在这个promise在面试中感觉就像“css清除浮动”一样,属于答不上来就会挂掉的前端基础知识了。

本文大纲:

1.promise基本用法;

2.promise A+手动实现

3.promise 使用中的哪些坑 promise.all 回调地狱等;

(一)promise基本用法

promise对象简单的来说,有点类似于ajax,它可以看做是一个装有某个未来才会结束的事件的容器。它有两个特点:1,promise的状态不受外部影响;2.promise的状态一旦改变,就不会再变了。

可以先看下promise的结构

function Promise(executor){

var self = this

self.status = 'pending' // Promise当前的状态

self.data = undefined // Promise的值

self.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面

self.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面

executor(resolve, reject) // 执行executor并传入相应的参数

}

promise的结构简单来说就是,它有个status表示三种状态**,pending(挂起),resolve(完成),reject(拒绝)。除此之外,它还有两个回调方法onResolvedCallback** 和 onRejectedCallback,分别对应resolve(完成) 和reject(拒绝)。

假如前面面试官问你promise的概念和基本用法,你像我一样提到了ajax的话,面试官很可能就会顺口问一下ajax(毕竟也是前端应该掌握得基础知识之一)。根据我的经验来看,一线大厂的面试官这个时候很可能会要你用promise撸一个ajax出来,一来可以考察你promise和ajax掌握得怎么样,二来可以考察你的代码能力。

1.1用promise来实现Ajax

const $ = (function(){

const ajax = function(url, async = false, type = 'GET'){

const promise = new Promise(function(resolve, reject){

const handler = function(){

if(this.readyState !== 4){

return;

}

if(this.status === 200){

resolve(this.response);

}else{

reject(new Error(this.statusText));

}

}

const client = new XMLHttpRequest();

client.open(type, url);

client.onreadystatechange = handler;

client.responseType = 'json';

client.setRequestHeader("Accept", "application/json");

client.send();

})

return promise;

}

return {

ajax

};

})()

调用方式:

$.ajax("/posts.json").then(function(json) {

console.log('Contents: ' + json);

}, function(error) {

console.error('出错了', error);

});

(二)手动实现一个 Promise/A

要注意的几个点:1.*then方法会返回一个新的promise,因此then方法应该写到原型链上。2.promise 的返回值或者抛出的err 会有传递现象。

例如:

new Promise(resolve=>resolve(8))
.then()
.catch()
.then(function(value) {
alert(value)
})

// 根据promise的定义和调用方式,可以先写出promise的数据结构

function Promise(executor){

const _this = this;

_this.status = 'pending';

_ths.data = undefined;

_this.onRejectedCallback = [];

_this.onResolvedCallback = [];

function resolve(value){

if(_this.status === 'pending'){

_this.status = 'resolved';

_this.data = value;

for(let i=0;i<_this.onResolvedCallback.length;i++){

_this.onResolvedCallback[i](value);

}

}

}

function reject(reason){

if(_this.status === 'pending'){

_this.status = 'rejected';

_this.data = reason;

for(let i=0;i<_this.onResolvedCallback.length;i++){

_this.onRejectedCallback[i](reason);

}

}

}

try{

executor(resolve, reject);

}catch (e){

reject(e)

}

}

// then方法应该写在原型链上

Promise.prototype.then = function(onResolved, onRejected){

const self = this;

// 要判断onResolved 和 onRejected是不是方法

onResolved = typeof onResolved === 'function' ? onResolved : function(value) { return value }

onRejected = typeof onRejected === 'function' ? onRejected : function(reason) { return reason }

if(self.status === 'resolved'){

return new Promise(function(resolve, reject){

try{

const resoult = onResolved([self.data](https://link.zhihu.com/?target=http%3A//self.data));

if( resoult instanceof Promise ){ // 如果返回的是新的promise,那么用这个promise的痛恨方法

resoult.then(resolve, reject)

}

resolve(resoult) // 否则 直接讲返回值作为newPromise的结果

}.catch(e){

reject(e);

}

});

}

// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释

if (self.status === 'rejected') {

return new Promise(function(resolve, reject) {

try {

var resoult = onRejected([self.data](https://link.zhihu.com/?target=http%3A//self.data))

if (resoult instanceof Promise) {

resoult.then(resolve, reject)

}

} catch (e) {

reject(e)

}

})

}

if(self.status === 'pending'){

return new Promise(function(){});

}

if (self.status === 'pending') {

// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,

// 只能等到Promise的状态确定后,才能确实如何处理。

// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里

// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释

return Promise(function(resolve, reject) {

self.onResolvedCallback.push(function(value) {

try {

var x = onResolved([self.data](https://link.zhihu.com/?target=http%3A//self.data))

if (x instanceof Promise) {

x.then(resolve, reject)

}

} catch (e) {

reject(e)

}

})

self.onRejectedCallback.push(function(reason) {

try {

var x = onRejected([self.data](https://link.zhihu.com/?target=http%3A//self.data))

if (x instanceof Promise) {

x.then(resolve, reject)

}

} catch (e) {

reject(e)

}

})

})

}

}

(三)promise 的使用中应该要避免哪些坑

在面试的时候,如果能答出来promise的使用中可能会出现什么坑,已经如何避免这些坑。相信能够给面试官一个好印象,尤其是面试2年工作经验的岗位的时候,通过这些就能很好的和培训班毕业的假简历区分开来。而且这些注意的点也是我本人在项目中实实在在踩过的坑。

注意点1,不要刻意为了美化代码而避免使用嵌套结构。

很多promise的科普教程里,作者都会强调,为了代码的间接性,尽量不要使用嵌套结构。ES7里还为了处理这个事情,专门设计了async await语法。但很多新手,再没有充分理解业务的前提下,盲目的为了美化代码,为了避免“回调地狱”,经常会造成很大的问题。

1.1promis.all 的坑

设想一个场景,比如我们写的一个表单组件。如下图所示:

这里有三个选项组件,每个组件都对应一个字典。当时组里的一个实习生,为了简洁美化代码,在这样一个组件里使用的Promise.all()。

类似于这样:

fetchData1 = function (){ // 请求组件1的字典};

fetchData2 = function (){ // 请求组件2的字典};

fetchData3 = function (){ // 请求组件3的字典};

Promise.all([fetchData1, fetchData2, fetchData3 ]);

当时看这段代码,并没有发现什么问题。结果后来一生产环境,问题就出来,控件1硬是获取不到字典项,但是获取组件1字典的接口,怎么查都是好的。最后只能重新看一遍组件的源代码,才发现了问题。原理是控件2的接口出现了问题,导致于整个Promise.all请求报错。

所以,在使用promise.all的时候要注意:业务上没有必然关联的请求比如联动组件这种,一定不要使用promise.all。

2.回调地狱并不可怕,不要盲目的使用async await

下面是比较常见的前端代码:

asycn ()=>{

await 获取订单1的数据;

await 获取订单2的数据;

......

}

当订单2的数据与订单1的数据直接没有相互依赖的关系的时候。获取订单2的执行时间就多了一倍的订单1的时间。同样的道理,假如后面还有订单3,订单4,那浪费的时间就更多了。这也是会造成前端页面卡顿的主要原因。

面对这样的常考题型,我觉得也要认真对待,因为面试中如果仅仅只是背答案,也很可能会挂掉。假如面试官看出来你在背答案,他只需要把相关的知识点都问一下,或者让你手动实现一下,又或者问你在项目中遇到了什么坑,你是怎么处理的。准备不充分的面试者,一下子就会露出马脚。

所以小编为大家准备了前端面试题资料,之前小编把前端面试问题的知识点整理成PDF文档,方便自己查阅学习,现在免费分享给大家,小编新建了一个前端学习圈,欢迎大家加入学习聊天哦~希望大家在里面有所收获也聊得开心!小伙伴们点击这里进群玩并取资料哦!


最近和小伙伴聊天了解到面试问题问vue比较多些,小编把vue相关的面试资料一起分享给大家,也是一样点击这里免费获取哦!


篇幅有限,小编没有展示完,需要文章中出现的全部资料的,点击获取来源:前端面试题资料就好喽,祝各位能在自己的人生找到属于自己的职场生涯!

喜欢看这种面试题详解的,请大家评论转发点赞告诉我哦,小编在这谢谢大家啦!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

前端面试之问到promise怎么办? 的相关文章

  • 【在VScode中使用终端命令编译运行c++程序】

    在VScode下的powershell下使用命令行编译运行c 43 43 文件 xff1a 首先 xff0c 进入文件所在的路径地址 xff1a 编译生成 exe可执行文件的不同方式 xff1a 不指定生成可执行文件的名称 xff1a 执行
  • 连接字符串strcat()函数

    strcat 函数的原型 xff1a span class hljs preprocessor include lt string h gt span span class hljs keyword char span span class
  • makefile '@' '$' '$$' '-' '-n ' 使用小结

    编译代码时 xff0c 会进程接触makefile xff0c 会涉及到一些符号 xff0c 由于不明白有些意思经常出现问题 xff0c 在此归纳一下 39 64 39 符号的使用 通常makefile会将其执行的命令行在执行前输出到屏幕上
  • 【STM32】RTC实时时钟,步骤超细详解,一文看懂RTC

    什么是RTC RTC Real Time Clock xff1a 实时时钟 RTC是个独立的定时器 RTC模块拥有一个连续计数的计数器 xff0c 在相应的软件配置下 xff0c 可以提供时钟日历的功能 修改计数器的值可以重新设置当前时间和
  • FreeRTOS学习(一)

    前言 最近正在学习和FreeRTOS相关的知识 xff0c 在此记录一下 xff0c 学习资料来自正点原子 在学习之前 xff0c 我也有一个和很多初学者共同的疑惑 Why RTOS xff1f 在探究这个问题之前 xff0c 我想先回顾一
  • stm32CUBEIDE 生成的串口初始化代码详解

    static void MX USART1 UART Init void huart1 Instance 61 USART1 huart1 Init BaudRate 61 115200 huart1 Init WordLength 61
  • Install Python 3 on Ubuntu 18.04 or 20.04

    Install Python 3 on Ubuntu 18 04 or 20 04 Step1 Check your version of PythonStep 2 Install Supporting SoftwareStep 3 Dow
  • Python 报错:Command "python setup.py egg_info" failed with error code 1

    最近做一个项目 xff0c 需要搭建很多不同的python环境 xff0c 于是我就用pip install装了很多包 xff0c 但是装包的时候我遇到了很多次这个报错 xff0c 比如这一次我在安装imgaug的时候也提示了这个错误 如图
  • FreeRTOS 常用的几个函数

    1 xff09 vTaskSuspend TaskHandle t Task ID 挂起指定任务 被挂起的任务绝不会得到CPU的使用权 xff0c 不管该任务具有什么优先 级 使用实例 static TaskHandle t LED Tas
  • FreeRTOS 任务设计注意事项

    1 FreeRTOS中程序运行的上下文包括 xff1a 中断服务函数普通任务空闲任务 1 xff09 中断服务函数是一种需要特别注意的上下文环境 xff0c 它运行在非任务的执行环境下 xff08 一般为芯片的一种特殊运行模式 xff08
  • 2022,程序员应该如何找工作

    最近找工作面了不少公司 xff0c 也有不少感悟和心得 xff0c 今天在这里分享给大家 1 想清楚自己为什么离职 每个人离职都有自己的理由 xff0c 这里列举了一些离职理由 钱给的不够干的不开心没有发展前途加班太严重回老家发展领导不好不
  • 我的四轴飞行器经验总结(一)

    从我看到了TED的演讲和不断冒出来大疆的无人机产品新闻开始 xff0c 我开始爱上了做四轴飞行器 xff0c 有的人可能只当做是一个电子产品制作或者DIY什么的 xff0c 可是我觉的我对四轴飞行器有着更加深的感情 xff0c 就连我的桌面
  • 环境变量设置后不生效

    不需要重启系统 xff0c 只需要重启VS
  • mac os上编译vlc视频库的踩坑之旅

    mac os上编译vlc视频库的踩坑之旅 mac os上编译vlc视频库的踩坑之旅 开始编译VLC视频库 一前期准备工作二参照官方编译文档安装软件三开始编译vlc四踩一些坑五总结 新项目开始目涉及媒体播放 xff0c 在android上多媒
  • 【JAVA】Eclipse保存时出现“Save could not be completed”问题

    问题 xff1a Save could not be completed 原因 xff1a eclipse的默认编译语言是 34 ISO 8859 1 34 xff0c 这个语言不支持中文 xff0c 所以如果编辑的程序含有中文而且编译语言
  •  MX-Linux:在distrowatch上的排名,为什么能够做到第一?

    个人观点 xff1a MX Linux 很好 MX Linux 桌面操作系统的成功 xff0c 主要有以下这几个因素 xff1a A 技术因素 xff1a 1 关键因素 xff1a MX snapshots工具 xff1b 2 次要因素 x
  • Qt 出现“程序异常结束”问题可能的解决思路

    第三方库的编译有问题 请注意 xff0c 出现此种问题绝不止这一个原因 xff0c 还包括其它很多原因 xff0c 这些原因可能千奇百怪 xff0c 需要你的开发经验去积累去发掘 在这里 xff0c 根据我自己的经验 xff0c 总结几种可
  • Qt 中如何在主窗口中添加子窗口

    方法 原理其实简单 和在窗口上动态 代码的形式 添加控件的方法一样 但需要设置一下子窗口的属性 在子窗口构造函数中添加代码 setWindowFlags Qt FramelessWindowHint 作用 隐藏子窗口的标题栏和边框 如果不隐
  • Qt5 自定义字体修改: 字体、大小以及颜色(部分要点已实测)

    Qt设置字体类型及添加字体文件 Qt 添加字体文件 1 设置支持的字体 QFont font font setFamily 34 填写字体名称 34 2 通过字体文件来设置字体 字体的名称可以是自带的 xff0c 也可以是外部的 xff0c
  • Qt QTableWidget 表格自适应 高度和宽度

    1 在MainWindow中设置 对被嵌入的子窗口进行设置 xff0c 去除子窗口的一些影响到嵌入的部件 pTable gt setWindowFlags Qt CustomizeWindowHint Qt FramelessWindowH

随机推荐

  • 互斥信号量和二值信号量的区别

    详解互斥信号量的概念和运行 https blog csdn net weichushun article details 122744773 互斥信号量的主要作用是对资源实现互斥访问 xff0c 使用二值信号量也可以实现互斥访问的功能 xf
  • Qt QTableWidget设置表头、菜单 背景色,以及不成功的原因

    Qt QTableWidget设置表头背景色不成功的原因 QTableWidget没有设置背景色的函数 xff0c 通过Qss样式来设置背景色 m pTable gt horizontalHeader gt setStyleSheet 34
  • Qt记住上次窗口的位置和状态

    include lt QCloseEvent gt include lt QShowEvent gt void MainWindow showEvent QShowEvent event restoreGeometry config gt
  • git-cola 使用方法

    目录 git cola 的用法实践记录 git cola 是 git的图形界面管理工具 因此 xff0c 在安装 git cola之前 xff0c 一般首先需要安装 git 官网地址 xff1a http git cola github i
  • Qt中的 DEPENDPATH 和 INCLUDEPATH 的区别

    在Qt中添加库文件的时候 xff0c Qt会自动在pro文件里生成三行配置 INCLUDEPATH 43 61 dir DEPENDPATH 43 61 dir LIBS 43 61 Ldir llibxxx includepath 和 l
  • STM32--MPU内存保护单元(一)

    先说明一下MPU xff0c MPU有很多含义 xff0c 我们常见的有 xff1a MPU xff1a Memory Protection Unit xff0c 内存保护单元 xff08 本文描述的内容 xff09 xff1b MPU x
  • Qt .pro 官方手册 Creating Project Files (*)

    Creating Project Files Qt 6 5 Creating Project Files qmake Manual Creating Project Files Qt 5 14 Qt 5 14 qmake Manual Cr
  • 感悟 编程思想:Rust,不同于面向过程思想与面向对象思想 (**)

    编程思想的演变 面向过程思想 xff1f 面向对象思想 xff1f Rust语言 xff0c 据说既有面向过程的特征 xff0c 又有面向对象的特点 xff1f 不要过分地拘泥于在一个项目中采用面向过程思想与面向对象思想 实际上 xff0c
  • 基础数据结构:单链表

    定义 单链表是一种线性数据结构 xff0c 用一组地址任意存储单元来存储数据 xff0c 存储单元分散在内存任意地址上 xff0c 存储单元之间用指针连接 单链表一般有两种 xff1a 带头结点的 xff0c 头结点不存放数据 xff0c
  • 开源飞控种类分享

    开源飞控发展 一 开源飞控发展 第一代开源飞控系统使用Arduino或其他类似开源电子平台为基础 xff0c 扩展连接各种MEMS传感器 xff0c 能够让无人机能平稳地飞起来 第二代开源飞控系统大多拥有自己的开源硬件 开发环境和社区 xf
  • 2020电赛绕障飞行无人机无遥控自动飞行解决方案(Ti)

    一 方案目标 针对部分大赛要求无人机在无遥控的情况下实现自主飞行 xff0c 本方案实现无遥控和接收机 情况下 xff0c 通过杜邦线或者按钮触发无人机进入相应模式 xff0c 执行模式对应的指令 由于 Ti 飞控有 8 个 PWM 接口
  • 数组名a+1和&a+1的区别

    C C 43 43 里面的数组名字会退化为指针 xff0c 所以数组名a实际指的是数组的第一个元素的地址 而数组名作为指针来讲有特殊性 xff0c 它正在它所指向的内存区域中 xff0c amp a的值和a的数值是相同的 xff08 可以输
  • float精度分析

    二进制浮点数是以 符号 43 数值表示法 储存 xff1a 将最高位指定为符号位 xff08 sign bit xff09 xff1b 指数部份 xff1a 即次高的e位 决定数值的数量级 小数部份 xff1a 即余下的f位 决定数值的浮动
  • Eigen库四元数表示顺序

    Eigen Quaterniond Q 1 2 3 4 表示顺序为 xff1a w 61 1 x 61 2 y 61 3 z 61 4 同理其余常用四元数数组顺序为q w x y z euler 61 R eulerAngles xff08
  • mavros中的一些坑

    固件 xff1a px4 控制 xff1a 通过对mavros发布mavros setpoint raw local话题控制无人机飞行 获取飞机的ENU坐标系坐标有两个话题 xff1a mavros global position loca
  • rosdep update 超时失败2021最新解决方法

    好记性不如烂笔头 xff0c 记录方法 xff0c 方便大家 一 关于 rosdep 安装ros的最后一步是rosdep init和rosdep update xff0c rosdep是解决ros包依赖问题的一个工具 rosdep init
  • STM32 FSMC/FMC原理保姆级讲解(一)

    FSMC通俗讲解 FSMC 框图FMC引脚说明FMC地址映射FSMC不同位宽操作FSMC寄存器FSMC时钟FSMC 四种模式FSMC参数设置FSMC 控制异步 NOR FLASH 的时序FSMC的功能FSMC的特点 STM32 FSMC F
  • Vue2.0生命周期和钩子函数的一些理解

    vue生命周期简介 生命周期详解 生命周期探究 对于执行顺序和什么时候执行 xff0c 看上面两个图基本有个了解了 下面我们将结合代码去看看钩子函数的执行 ps 下面代码可以直接复制出去执行 span class xml span clas
  • Python变量的下划线

    1 两边双下划线 xff1a 特殊变量 xff0c 类似 xxx xff0c 可以直接访问 xff0c 不是private变量 xff0c 不能用 name score 这样的变量名 2 左侧单下划线 xff1a 如 xff0c name
  • 前端面试之问到promise怎么办?

    前言 Promise作为面试中的经典考题 xff0c 我们一定要深刻学习和理解它 xff01 Promise有什么用呢 xff1f 答 xff1a 我们拿它解决异步回调问题 Pomise是ES6里面新增的一种异步编程的解决方案 现在这个pr