【面试题】Promise只会概念远远不够,还需这17道题目巩固!

news2024/11/25 2:18:24

 前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

 表妹一键制作自己的五星红旗国庆头像,超好看

在学习Promise相关题目之前,我们先做一些知识的回顾:JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。 JS执行机制中有同步任务异步任务,执行入队优先级是前者;异步任务又分为宏任务微任务,执行入队优先级是后者。我们先看一下下面例子来充分理解一下JavaScript执行机制:(如代码所示。如图所示)

console.log(1)
setTimeout(() => {
        console.log(2)
}, 0)
const promise = new Promise((resolve, reject) => {
    console.log(3);
    resolve(4)
})
promise.then((res) => {
    console.log(res)
})
// 1 3 4 2

image.png

  • 同步任务只有前一个任务执行完毕,才能执行后一个任务。还记得大学饭堂排队打饭例子吗,作为一个优秀的当代大学生,排队打饭遵循先来先打后来排队原则,只有前面那一个同学打完饭,你才能打。当然,你插队就另说😜。
  • 异步任务由JavaScript 委托给宿主环境进行执行。当异步任务执行完成后,会通知JavaScript 主线程执行异步任务的回调函数。还记得铁板烧吗,说实际的,铁板烧确实不错,细心的你有没有发现,老板里有多个锅,不可能只有一个锅,每一份铁板烧都需要时间,不然让顾客等待得花儿都谢了,你下次也不会来了,所以多个锅就代表多个任务,不需要等待一个锅烧完才去重新烧,也就是说不需要等待当前任务结束(这个任务没有那么快完成,未来某个时间点才结束,它就是异步任务),为节省时间或能耗,可以继续去执行其他任务。
  • 宏任务:JavaScript自身发起。如setTimeout 、setInterval MessageChannel I/O、setImmediate(Node环境)、script(整体代码块)
  • 微任务:是由宿主(浏览器、Node)发起的。MutationObserver(浏览器环境)、promise.[ then/catch/finally ]、事件队列 process.nextTick(Node环境)
  • Promise:异步编程的一种解决方案,可以通俗把它当作一个容器,内部存储着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上来讲它就是一个对象,有两个特点:

    有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

    • 对象状态不受外界影响
    • 状态一旦改变就不会在变,也就是说任何时候Promise都只有一种状态

话不多说,我们直接上题目实操吧,一步步理解!

题目1️⃣:Promise没有resolve前都是同步任务。 Promise是微任务,但是Promise构造器内有同步任务,js线程会先把同步任务执行完,再去执行resolve回调。

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve(2)
    console.log(3);
});
promise.then((res) => {
    console.log(res);
});
console.log(4, promise);// 4 fulfilled
// 1 3 4 2

题目2️⃣:当函数返回的是一个Promise实例

const fn = () => (new Promise((resolve, reject) => {
        console.log(1);
        resolve(2)
}))
fn().then(res => {
   console.log(res)
})
console.log(3)
// 1 3 2

题目3️⃣:宏任务与微任务执行顺序。 定时器是宏任务,Promise是微任务,其他都是同步任务,

console.log(1)
setTimeout(() => {
     console.log(2)
})
Promise.resolve().then(() => {
     console.log(3)
})
console.log(4)
// 1 4 3 2

题目4️⃣:当微任务中嵌套宏任务、宏任务嵌套微任务、宏任务嵌套宏任务、微任务嵌套微任务时

  • 当微任务中嵌套宏任务时:由于构造器中除了resolve执行回调之外,还有其他同步任务、宏任务。

    // 当微任务中嵌套宏任务
    const promise = new Promise((resolve, reject) => {
        console.log(1);
        setTimeout(() => {
            console.log(2);
            resolve(3);
            console.log(4);
        }, 0);
        console.log(5);
    });
    promise.then((res) => {
        console.log(res);
    });
    console.log(6);
    // 1 5 6 2 4 3
    
  • 当宏任务嵌套宏任务时:当发生嵌套任务时,优先处理同层级,因为程序是自上而下的,setTimeout是异步任务中的宏任务,同层级的比嵌套的先入队,所以先执行同层级。

    // 宏任务嵌套宏任务
    setTimeout(() => {
        console.log(1);
        setTimeout(() => {
           console.log(2)
        }, 0)
    }, 0)
    setTimeout(() => {
        console.log(3)
    }, 0)
    console.log(4)
    // 4 1 3 2
    
  • 当宏任务嵌套微任务时

     // 宏任务嵌套微任务
    setTimeout(() => {
        console.log(1);
        // 微任务
        Promise.resolve().then(() => {
                console.log(2)
        })
    }, 0)
    setTimeout(() => {
         console.log(3)
    }, 0)
    console.log(4)
    // 4 1 2 3
    
  • 微任务嵌套微任务时:同层级微任务优先执行。因为任务队列先入队都是同级别的。

    // 微任务链接微任务
    Promise.resolve().then(() => {
            console.log(1)
            return 2;
    }).then((res) => {
            console.log(res);
    })
    Promise.resolve().then(() => {
            console.log(3)
    })
    // 1 3 2
    
    // 微任务嵌套微任务
    Promise.resolve().then(() => {
            console.log(1)
            Promise.resolve().then(() => {
                    console.log(2)
            })
            return 3
    }).then((res) => {
            console.log(res);
    })
    Promise.resolve().then(() => {
            console.log(4)
    })
    // 1 4 2 3
    

题目5️⃣:结合微任务和宏任务,灵活理解Promise三种状态。从代码中我们可以看出例子中的单纯是同步任务有2 3 4,而宏任务中的同步任务是1,当执行程序中同步任务时,微任务还没有resolve回调函数,所以promise对象都是pending状态,由于抛出错误是微任务中的宏任务,所以优先执行,然后再执行全局的setTimeout,最后promise1是fufilled状态,promise2是rejected状态。

const promise1 = new Promise((resolve, reject) => {
   setTimeout(() => {
           resolve("success");
           console.log(1);
   }, 1000);
   console.log(2);
});
const promise2 = promise1.then(() => {
       throw new Error("error!!!");
});
console.log(3, promise1);// pending
console.log(4, promise2);// pending
setTimeout(() => {
   console.log(5);
   console.log(6, promise1);// fufilled
   console.log(7, promise2);// rejected
}, 2000);
// 2 3 4 1 抛出error! 5 6 7

题目6️⃣:Promise中构造函数中的resolve或reject只有第一次执行有效。

const promise = new Promise((resolve, reject) => {
    resolve(1);
    reject("error");
    resolve(2);
});
promise.then(res => {
    console.log("then: ", res);
}).catch(err => {
    console.log("catch: ", err);
})
// then:1

题目7️⃣:Promise对象中的catch无视链接位置,都能捕获上层未捕捉过的错误,then3会执行因为catch会返回一个Promise,且由于这个Promise没有返回值,所以打印出来的是undefined。

const promise = new Promise((resolve, reject) => {
        reject("error");
        resolve(1);
});
promise.then(res => {
        console.log("then1: ", res);
}).then(res => {
        console.log("then2: ", res);
}).catch(err => {
        console.log("catch: ", err);
}).then(res => {
        console.log("then3: ", res);
})
// catch:  error
// then3:  undefined

题目8️⃣:Promise对象的链式调用的执行顺序。Promise可以链式调用,不过promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用,return 2会被包装为resolve(2)。

Promise.resolve(1)
.then(res => {
        console.log(res);
        return 2;
})
.catch(err => {
        return 3;
})
.then(res => {
        console.log(res);
});
// 1 2

题目9️⃣:注意then的第二参数错误处理与catch的区别。,两个都是处理reject状态回调结果或者是抛出的错误,如果存在第二参数,也存在catch,捕获错误是参数生效,否则就会catch生效。也可以这样理解then的第一个参数是处理成功的函数,第二个参数是处理失败的函数。如果两个都没有就会直接报错。

Promise.reject('err!!!')
    .then((res) => {
            console.log('success', res)
    }, (err) => {
            console.log('error', err)
    }).catch(err => {
            console.log('catch', err)
    })
// error err!!!


Promise.resolve()
.then(() => {
  throw new Error('error!!!');
})
.then(
  function success(res) {},
  function fail1(err) {
    console.log('fail1', err);
  }
)
.catch(function fail2(err) {
  console.log('fail2', err);
});
// fail1 Error: error!!!

题目🔟:then参数是函数,对于非函数会出现值穿透。 如果then传入的是非函数,resolve会被传到是函数的地方。

Promise.resolve(1).then(2).then(Promise.resolve(3))
.then(console.log)
// 1

题目1️⃣1️⃣:finally方法也是一个微任务。:.finally()方法不管Promise对象最后的状态如何都会执行。.finally()方法的回调函数不接受任何的参数。

function promise1() {
        let p = new Promise((resolve) => {
                console.log(1);
                resolve(2)
        })
        return p;
}
function promise2() {
        return new Promise((resolve, reject) => {
                reject('error')
        })
}
promise1()
        .then(res => console.log(res))
        .catch(err => console.log(err))
        .finally(() => console.log('finally1'))

promise2()
        .then(res => console.log(res))
        .catch(err => console.log(err))
        .finally(() => console.log('finally2'))
// 1 2 error finally1 finally2

题目1️⃣2️⃣:async await执行机制:在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,因此相当于一直在await,await,await却始终没有响应,所以就不能执行await后面的语句了。

  • async await宏任务:await强制的是当前async函数域,所以不能优先处理await后面的语句,全局同步任务可优先处理。

    async function async1() {
            console.log(1);
            await async2();
            console.log(2);
    }
    async function async2() {
        setTimeout(() => {
           console.log(3)
        }, 0)
        console.log(4);
    }
    async1();
    console.log(5)
    // 1 4 5 2 3
    
  • async await微任务

    async function async1() {
        console.log(1);
        await new Promise(resolve => {
            console.log(2)
        })
        // Promise没有resolve所以一直处于pending
        console.log(3);
        return 'async1 end'
    }
    console.log(4)
    async1().then(res => console.log(res))
    console.log(5)
    // 4 1 2 5
    
  • async await微任务、宏任务

    async function testSometing() {
            console.log(1);
            return 2;
    }
    async function testAsync() {
            console.log(3);
            return Promise.resolve(4);
    }
    async function test() {
            console.log(5);
            const v1 = await testSometing();
            console.log(v1);
            const v2 = await testAsync();
            console.log(v2);
            console.log(v1, v2);
    }
    test();
    var promise = new Promise(resolve => {
            console.log(6);
            resolve(7);
    });
    promise.then(val => console.log(val));
    console.log(8);
    // 5 1 6 8 2 3 7 4
    // 2 4
    

题目1️⃣3️⃣:理解Promise.all方法,all方需要等所有异步操作执行完后才执行回调,由于有reject状态的回调,所以没有执行then,直接执行了catch。

function runAsync(x) {
        const p = new Promise(r => setTimeout(() => r(x, console.log('runAsync', x)), 1000))
        return p
}
function runReject(x) {
        const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log('runRejct', x)), 1000 * x))
        return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
        .then(res => console.log('then:', res))
        .catch(err => console.log('catch:', err))
// runAsync 1
// runAsync 3
// runRejct 2
// catch: Error: 2
// runRejct 4

问题1️⃣4️⃣:理解Promise.race方法,race获取最快的哪一个异步操作的结果。由于下面执行了一个reject状态的回调,所以没有执行then,如果没有这个runReject(0),下面例子打印的是1 result:1 2 3。

function runAsync(x) {
const p = new Promise(r =>
        setTimeout(() => r(x, console.log(x)), 1000)
);
return p;
}
function runReject(x) {
const p = new Promise((res, rej) =>
        setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
);
return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log("result: ", res))
.catch(err => console.log(err));
// 0 Error:0 1 2 3

问题1️⃣5️⃣: 构造函数里resolve之前的同步任务打印3 7优先执行,然后就是first后面的全局同步任务4,注意这里与上面的嵌套微任务的区别,因为这里直接resolve(2)的话first就会完成回调函数了,但是最外层构造函数内还有一个微任务p,所以先执行。

const first = () => (new Promise((resolve, reject) => {
        console.log(3);
        let p = new Promise((resolve, reject) => {
                console.log(7);
                setTimeout(() => {
                        console.log(5);
                        resolve(6);
                        console.log(p)
                }, 0)
                resolve(1);
        });
        resolve(2);
        p.then((arg) => {
                console.log(arg);
        });
}));
first().then((arg) => {
        console.log(arg);
});
console.log(4);
// 3 7 4 1 2 5 fulfilled:1

问题1️⃣6️⃣:综合Promise值穿透、宏任务、微任务、async await

const async1 = async () => {
    console.log(1);
    setTimeout(() => {
            console.log(2)
    }, 2000)
    await new Promise(resolve => {
            console.log(3)
    })
    console.log(4)
    return 5
}
console.log(6);
async1().then(res => console.log(res));
console.log(7);
Promise.resolve(8)
    .then(9)
    .then(Promise.resolve(10))
    .catch(11)
    .then(res => console.log(res))
setTimeout(() => {
    console.log(12)
}, 1000)
//6 1 3 7 8 12 2

问题1️⃣7️⃣: Promise的状态一旦改变就无法改变。.finally的返回值如果在没有抛出错误的情况下默认会是上一个Promise的返回值。由于微任务Promise中有宏任务,并且有多个resolve,但是Promise值只能回调一个函数,所以打印了3,.finally也是一个微任务并且不接收回调函数参数,所以为undefined,然后执行微任务Promise中宏任务,接着执行then中的宏任务,此时p1状态是undefined(没有then处理)。

const p1 = new Promise((resolve) => {
    setTimeout(() => {
            resolve(1);
            console.log(2)
    }, 0)
    resolve(3);
    resolve(4);
}).then(res => {
    console.log(res)// 打印3
    setTimeout(() => {
        console.log(p1)
    }, 1000)
}).finally(res => {
    console.log('finally:', res)
})
// 3 finally:undefined 2 fulfilled:undefined

✍总结:其实学完这17道题目,希望各位同仁有收获,对于Promise的执行有更深理解与掌握。接下来就总结一下如何快速处理这种题目的技巧(从上到下按顺序理解):

  • 🚩 先看是否同步任务对于非函数,先看当前语句是否是同步任务,是就先执行;对于函数,不管是同步还是异步操作,也是先执行同步任务。

  • 🚩 异步任务判断是宏任务还是为微任务先执行微任务,后执行宏任务。

  • 🚩 任务嵌套情况,灵活处理不管是微任务还是宏任务,先处理同级别任务,但是对于微任务有些情况特殊需要灵活理解。

  • 🚩 Promise特点要重视

    • 对象状态不受外界影响;状态一旦改变就不会再变。
    • then传参必须是函数,否则出现值穿透。
    • 理解catch处理与then第二个参数处理。
    • Promise如果没有resolve或reject,就会一直处于pending状态。
    • 区分all方法与race方法。
  • 🚩 理解async await强制执行函数体内await当前语句完毕(也相当于一个微任务),才会执行函数体内await后面的语句。

 前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

 表妹一键制作自己的五星红旗国庆头像,超好看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1043817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化测试面试经历

一家做户外的外企 面试问题 1、自我介绍:大概介绍了自己的工作经历 2、数据库问题:学生表中包含id、姓名、成绩、班级,求平均成绩 回答:group by 班级,求平均(不够完美) 3、java的访问修饰符…

MQ - 32 基础功能:消息查询的设计

文章目录 导图概述什么时候会用到消息查询消息队列支持查询的理论基础消息数据存储结构关于索引的一些知识点内核支持简单查询根据 Offset 查询数据根据时间戳查询数据根据消息 ID 查询数据借助第三方工具实现复杂查询第三方引擎支持查询工具化简单查询总结导图 概述 从功能上…

Error:java: 错误: 不支持发行版本 5

当创建maven项目之后,编译一个简单的helloworld,发生以下报错 : Error:java: 错误: 不支持发行版本 5 解决方案 : File -> Settings -> BUIld,Execution,Deployment -> Compiler -> Java Compiler 将Module表格中的Target bytecode version的1.5换…

【优测云服务平台】打造承载百倍级增长后台背后的力量-性能优化

项目介绍: 腾讯课堂是腾讯推出的专业在线教育平台,凭借技术优势,实现在线即时互动教学,打破地域的限制,让每个人都能接受优秀老师的指导和教学。 一、背景 2020年初,新冠病毒肆虐,疫情大面积爆…

基于SSM的办公用品管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

cocoscreator3.X 强更 游戏内下载APK和安装APK

本文环境3.6.0,目测3.7, 3.8都可以用 强制更新是强制用户下载整包并覆盖安装,因为android部分代码不方便热更,所以游戏内采用服务器推送下载通知,游戏执行下载后再安装的形式. 下载完全可在ts层完成,可采用cocoscrea…

继苹果、联发科后,传高通下一代5G芯片将由台积电以3纳米代工

台积电3纳米又有重量级客户加入。市场传出,继苹果、联发科之后,手机芯片大厂高通下一代5G旗舰芯片也将交由台积电以3纳米生产,最快将于10月下旬发表,成为台积电3纳米第三家客户。 针对相关传闻,至昨日(25日…

基于SpringBoot的药房管理系统

基于SpringBootVue的药房管理系统,前后端分离 开发语言:Java数据库:MySQL技术:SpringBoot、Vue、Mybaits Plus、ELementUI工具:IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色:管理员、用户 管理员&am…

共享门店模式:一种新兴的商业模式

共享门店模式是一种利用实体店铺的空间和资源,让多个品牌或商家在同一地点共同运营的商业模式。这种模式可以提高店铺的利用率,降低经营成本,增加客流量,实现资源的最大化利用。如果你是一个有创业想法的企业家,或者你…

Webshell 流量特征分析 (2)

前言:webshell是以asp、php、jsp或者cgi等网页文件形式存在的一种代码执行环境,主要用于网站管理、服务器管理、权限管理等操作。使用方法简单,只需上传一个代码文件,通过网址访问,便可进行很多日常操作,极…

华为OD七日集训第6期 十一特辑 - 按算法分类,由易到难,循序渐进,玩转OD

目录 专栏导读华为OD机试算法题太多了,知识点繁杂,如何刷题更有效率呢? 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、优先队列4、滑动窗口5、二叉树6、并查集7、栈 三、算法1、基础算法① 贪心算法② 二分查找③ 分治…

【ComfyUI】Pytorch预训练模型(torch.hub)缓存地址修改

序言 最近玩ComfyUI时,每次生成图片,总是会下载一些东西,时间长了,C盘就不够用了,今天清理C盘发现,总是会在C:\Users\yutao\.cache\torch\hub\checkpoints这个路径下,下载大模型文件&#xff0…

初级软件测试入门教程

一、软件测试的基本概念 1、软件测试的定义 就是以发现错误为目的而运行程序的过程。 软件测试员的目标是找到软件缺陷,尽可能早一些,并确保其得以修复。 2、软件测试方法总体分类 试图验证软件是“工作的”(所谓“工作的”就是指软件的…

linux应用层静态链接和动态链接(.a .so)

1、介绍 即使一个非常简单的程序,也需要依赖C标准库和系统库,链接其实就是把其他第三方库和自己源代码生成的二进制目标文件融合在一起的过程。经过链接之后,那些第三方库中定义的函数就能被调用执行了。早期的一些操作系统一般使用静态链接…

代码随想录算法训练营第23期day7| 454.四数相加II 、383. 赎金信 、15. 三数之和、18. 四数之和

目录 一、(leetode 454)四数相加II 二、(leetcode 383)赎金信 暴力解法 哈希法 三、(leetcode 15)三数之和 四、(leetcode 18)四数之和 一、(leetode 454&#xf…

git 分支管理进阶

1. merge 命令:git merge A 作用:把 A 分支 合并到当前分支 (此时当前分支新增了一次提交,指着指向该提交) 初始状态: git merge bugFix 后: 此外,如果再把 main 分支合并到 bug…

Unigram,Bigram,N-gram介绍

Unigram,Bigram,N-gram介绍 Unigram,Bigram,N-gram这三个概念,在学习自然语言的过程中大家可能会遇到。 Unigram,Bigram,N-gram在自然语言内容中的语言模型部分中大家可能会碰到。语言模型有很多种,在上一篇介绍一个…

三层交换机与防火墙对接上网如何配置

环境: 1.三层交换机 H3C S6520 version 7.1.070, Release 6530P02 2.防火墙 深信服 8.0.75 AF-2000-FH2130B-SC 问题描述: 三层交换机与防火墙对接上网如何配置 公司有多个部门且位于不同网段,各部门均有访问Internet的需求。现要求用户通过三层交换机和防火墙访问…

MySQL 事务的操作指南(事务篇 二)

基本操作 事务的提交方式:自动提交(autocommit1)和手动提交(autocommit0) 查询和修改事务提交方式: -- 查看事务提交方式(标识表示这是个系统变量) select autocommit ;-- 修改事务提交方式为自动提交 …

Zorin OS 16.3 发布:无缝升级和卓越改进

导读Zorin OS 团队自豪地宣布了备受期待的 Zorin OS 16.3 版本的发布,这是这个受欢迎的 Linux 发行版的一个里程碑版本。自首次发布以来不到两年时间,Zorin OS 已经获得了庞大的用户群体,截至目前已经有 530 万次下载,而 16.3 版本…