面试题目_总结面试中 promise 相关题目的套路

Promise 作为当下主流的异步解决方案,在工作中和面试中常常出现,尤其是在面试中,会弄个场景让你手写代码,这里给大家介绍五道比较有代表性的题目,以便熟悉一些套路。

promise 简单介绍

先简单介绍下 Promise

Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。可以为异步操作的成功和失败绑定执行函数,让异步方法可以像同步方法一样返回值,但立即返回的是一个能代表未来可能出现结果的Promise对象。

Promise 对象有三种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

Promise 的使用和提供的静态方法:

  • new Promise( function(resolve, reject) {...} /* executor */ ); :返回 Promise 对象
  • Promise.all(iterable) :iterable参数对象里所有的promise对象都成功的时候才会触发成功,若一个失败,则立即触发返回Promise对象的失败
  • Promise.race(iterable):iterable参数中的一个成功或者失败都会立即触发返回对象的成功和失败
  • Promise.reject(reason):返回一个状态为失败的Promise对象
  • Promise.resolve(value):返回一个状态由value给定的Promise对象,通常用于将一个值以Promise的方式使用。

下面开始看题

题一

与js事件循环结合出题,如下,写出执行结果

console.log('script start')
async function async1() {
    await async2()
    console.log('async1 end')
}
async function async2() {console.log('async2 end')}
async1()
setTimeout(function () {console.log('setTimeout')}, 0)
new Promise(resolve => {
    console.log('Promise')
    resolve()
}).then(function () {
        console.log('promise1')
    }).then(function () {
        console.log('promise2')
    })
console.log('script end')
// 结果如下
// script start
// async2 end
// Promise
// script end
// async1 end
// promise1
// promise2
// setTimeout

掌握事件循环机制和明白 Promise.then() 属于微队列,这一类的题目就都是一个套路。

题二

实现如下调用,lazyMan('xxx').sleep(1000).eat('333').sleepFirst(2000) sleepFirst 最先执行。

这题考察如何组合多个 Promise 和链式调用。

可以用数组将 sleep eat 等函数暂存,同时为了能链式调用,所以每个函数需返回 Promise 对象。那么什么时候执行数组中的函数呢?

根据事件循环机制,我们用 setTimeout 来执行数组中的方法,在定时器的回调函数中相关的事件已经添加到数组中了,链式执行数组中方法前,需要有一个构建一个 Promise 对象来执行 then 方法,可以通过 Promise.resolve() 返回一个 Promise 对象。

function lazyMan(name) {
    this.task = [];
    this.task.push(() => {
        return new Promise(res => {
            console.log('name:' + name);res()
        })
    })
    let run = () => {
        let sequence = Promise.resolve()
        for (const func of this.task) {
            sequence = sequence.then(()=>func())
        }
    }
    setTimeout(() => {run()}, 0)
    this.eat = (str) => {
        this.task.push(() => {
            return new (res => {
                console.log('eat:' + str);res()
            })
        })
        return this;
    }
    this.sleep = (time) => {
        this.task.push(() => {
            return new Promise(res => {
                setTimeout(() => {
                    console.log(`Wake up after ` + time);res()
                }, time)
            })
        })
        return this;
    }
    this.sleepFirst = (time) => {
        this.task.unshift(() => {
            return new Promise(res => {
                setTimeout(() => {
                    console.log(`sleepFirst up after ` + time);res()
                }, time)
            })
        })
        return this;
    }
    return this;
}

题三

任务队列可不断的添加异步任务(异步任务都是Promise),但只能同时处理5个任务,5个一组执行完成后才能执行下一组,任务队列为空时暂停执行,当有新任务加入则自动执行。

class RunQune{
    constructor(){
        this.list = []; // 任务队列
        this.target = 5; // 并发数量
        this.flag = false; // 任务执行状态
        this.time = Date.now()
    }
    async sleep(time){
        return new Promise(res=>setTimeout(res,time))
    }
    // 执行任务
    async run(){
        while(this.list.length>0){
            this.flag = true;
            let runList = this.list.splice(0,this.target);
            this.time = Date.now()
            await this.runItem(runList)
            await this.sleep(300) // 模拟执行时间
        }
        this.flag = false;
    }
    async runItem(list){
        return new Promise((res)=>{
            while(list.length>0){
                const fn = list.shift();
                fn().then().finally(()=>{
                    if(list.length === 0){
                        res()
                    }
                })
            }
        })
    }
    // 添加任务
    push(task){
        this.list.push(...task);
        !this.flag && this.run()
    }
}

这题还可以进一步发散,不需要等待一组完成在执行下一组,只要并发量没有满,就可以加入新的任务执行,实现的思路没太大变化,在 finally 中改为新增任务。

题四

期望id按顺序打印 0 1 2 3 4 ,且只能修改 start 函数。

function start(id) {
    execute(id)
}
for (let i = 0; i 5; i++) {
    start(i);
}
function sleep() {
    const duration = Math.floor(Math.random() * 500);
    return new Promise(resolve => setTimeout(resolve, duration));
}
function execute(id) {
    return sleep().then(() => {
        console.log("id", id);
    });
}

id 的打印是个异步事件,在 setTimeout 回调执行,按照上面的代码,谁的倒计时先结束,id就先打印,那么想要id按顺序打印,就需要将多个异步事件同步执行,promise 的链式调用可以派上用场。代码如下

function start(id) {
    // execute(id)
    // 第一种:promise 链式调用,execute 函数返回的就是 promise ,所以可以利用这一点,通过 promise.then 依次执行下一个打印
    this.promise = this.promise ? this.promise.then(()=>execute(id)) : execute(id)

    // 第二种:先用数组存储异步函数,利用事件循环的下一个阶段,即 setTimeout 的回调函数中执行 promise 的链式调用,这方法本质上和第一种是一样的
    this.list = this.list ? this.list : []
    this.list.push(() => execute(id))
    this.t;
    if (this.t) clearTimeout(this.t)
    this.t = setTimeout(() => {
        this.list.reduce((re, fn) => re.then(() => fn()), Promise.resolve())
    })

    // 第三种:数组存储id的值,在通过 await 异步执行 execute 函数
    this.list = this.list ? this.list : []
    this.list.push(id)
    clearTimeout(this.t)
    this.t = setTimeout(async () => {
        let _id = this.list.shift()
        while (_id !== undefined) {
            await execute(_id);
            _id = this.list.shift()
        }
    })
}

题五

手撕源码系列,来手写一个Promise,在动手前需要先了解 Promise/A+ 规范,列举关键部分的规范,详细规范可见文末链接

  1. Promise 的状态:一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。
  2. 状态迁移:等待态可以迁移至执行态或者拒绝态;执行态和拒绝态不能迁移至其他状态,且必须有一个不可变的终值
  3. then 方法:一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因,then 方法可以被同一个 promise 调用多次。then 方法接收两个参数 onFulfilled, onRejected,onFulfilled 和 onRejected 必须被作为函数调用,且调用不可超过1次。then 方法需返回 Promise 对象

根据这三点我实现了一个简化版的 Promise

function MPromise(executor) {
    this.status = 'pending'; // pending , fulfilled , rejected 
    this.data = '' // 当前promise的值,主要用于 then 方法中的 fulfilled , rejected 两种状态的处理
    this.resolveFuncList = []; //  使用数组的原因是,一个promise可以同时执行多个 then 方法, 也就会同时存在多个then回调
    this.rejectFunc;
    const self = this;
    function resolve(value) {
        // 使用 setTimeout 实现异步
        setTimeout(() => {
            if (self.status === 'pending') {
                self.status = 'fulfilled';
                self.data = value;
                // 执行 resolve 函数
                self.resolveFuncList.forEach(func => {
                    func(value)
                });
            }
        })
    }

    function reject(reason) {
        setTimeout(() => {
            if (self.status === 'pending') {
                self.status = 'rejected';
                self.data = value;
                self.rejectFunc && self.rejectFunc(reason);
            }
        })
    }
    try {
        executor(resolve, reject)
    } catch (error) {
        reject(error)
    }
}

MPromise.prototype.then = function (onFulfilled, onRejected) {
    let promise2;
    // 区分不同状态下的处理
    if (this.status === 'pending') {
        return promise2 = new MPromise((res, rej) => {
            this.resolveFuncList.push(function (value) {
                let x = onFulfilled(value);
                resolvePromise(promise2, x, res, rej)
            })

            this.rejectFunc = function (reason) {
                let x = onRejected(reason);
                resolvePromise(promise2, x, res, rej)
            }
        })
    }
    if (this.status === 'fulfilled') {
        return promise2 = new MPromise((res, rej) => {
            setTimeout(() => {
                let x = onFulfilled(this.data) // 输出将上一次执行结果
                resolvePromise(promise2, x, res, rej)
            })
        })
    }
    if (this.status === 'rejected') {
        return promise2 = new MPromise((res, rej) => {
            setTimeout(() => {
                let x = onRejected(this.data)
                resolvePromise(promise2, x, res, rej)
            })
        })
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (x instanceof MPromise) {
        if (x.status === 'pending') {
            x.then(value => {
                resolvePromise(promise2, value, resolve, reject)
            }, reason => {
                reject(reason)
            })
        } else {
            x.then(resolve, reject)
        }
    } else {
        resolve(x)
    }
}

有的因为时间有限,会让手写 Promise 的 api,以下两个就常常被问到

1. 手写一个 Promise.all

/**
 * Promise.all Promise进行并行处理
 * 参数: promise对象组成的数组作为参数
 * 返回值: 返回一个Promise实例
 * 当这个数组里的所有promise对象全部进入FulFilled状态的时候,才会resolve。
 */
Promise.all = function(promises) {
    return new Promise((resolve, reject) => {
        let values = []
        let count = 0
        promises.forEach((promise, index) => {
            promise.then(value => {
                console.log('value:', value, 'index:', index)
                values[index] = value
                count++
                if (count === promises.length) {
                    resolve(values)
                }
            }, reject)
        })
    })
}

2. 手写一个 Promise.rase

/**
 * Promise.race
 * 参数: 接收 promise对象组成的数组作为参数
 * 返回值: 返回一个Promise实例
 * 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪一个更快)
 */
Promise.race = function(promises) {
    return new Promise((resolve, reject) => {
        promises.forEach((promise) => {
            promise.then(resolve, reject);
        });
    });
}

小结

文中代码略长,在电脑上查看效果更佳。

若能帮到大伙,期望能给个点赞鼓励~

欢迎大伙在公号对话框点击交个盆友,添加我微信一起探讨交流~

参考文章

https://www.ituring.com.cn/article/66566 

https://promisesaplus.com/

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

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

相关文章

javafx 自定义控件_JavaFX自定义控件– Nest Thermostat第1部分

javafx 自定义控件几周前,由于Hendrik Ebbers的出色文章 ,我决定花一些时间观看有关JavaFX的JavaOne讨论。 我不得不说我已经学到了很多东西,只是看这些视频(即使我还没有完成)! Gerrit的“使用力&#xf…

【渝粤教育】电大中专Office办公软件 (4)作业 题库

1.以下软件不属于系统软件的是( )。 A.Visual Studio 2019 B.MySQL 5.7 C.Windows 10 D.Office 2016 错误 正确答案:左边查询 学生答案:未作答 2.学习好Office办公软件这门课程,应该( )。 A.重视…

【渝粤教育】电大中专中药学基础作业 题库

试卷答案 1.首创按药物自然属性进行分类的本草著作是()。 A.《神农本草经》 B.《本草经集注》 C.《本草纲目》 D.《新修本草》 E.《本草拾遗》 正确 正确答案:左边查询 学生答案:B 2.载药数最多的本草著作是(&#xff…

不等号属于不等式吗_考研专业课备考时,仅仅多刷几遍目标院校的期末考试题就够吗?...

考研专业课备考时,仅仅多刷几遍目标院校的期末考试题就够吗?也许这要看各专业情况,部分专业的考研题和本科生的期末考试题难度类似,比如说人文社科类的专业,这也是我的猜测情况。大部分专业的专业课题目难度&#xff0…

使用Spring Boot和H2可以完全工作的原型

我们确实在弹簧上使用了很多h2,特别是对于单元测试。 但是,我们可能希望拥有一个功能齐全的原型来显示数据,而不是进行单元测试。 H2是最理想的选择,它在spring上运行良好,与大多数数据库都具有良好的语法兼容性&…

【渝粤教育】电大中专品牌管理与推广 (2)作业 题库

1通常,对品牌的排他专有性的保护手段主要是注册商标、申请专利、授权经营,等等。该说法() A正确 B错误 正确 正确答案:左边查询 学生答案:A 2品牌服务是以服务而不是以产品为主要特征的品牌,如商…

【渝粤教育】电大中专学前教育学作业 题库

1学前教育的孕育阶段的时间定位于() A21世纪 B16世纪以前 C远古时期 D18世纪 错误 正确答案:左边查询 学生答案:A 2我国封建社会第一部完整的家庭教科书《颜氏家训》出自() A陶行知 B昆体良 C颜之推 D朱熹 …

饿了吗商品列表_仅仅一字之差,饿了么起诉饿了吗

饿了么与“饿了吗”,仅仅一字之差,相信不少人乍看会以为是一家。但近日公开的一则判决书显示,因为太近似,二者曾对簿公堂。饿了吗公司以败诉收场,被判处立即变更其企业名称,变更后企业名称中不得含有与“饿…

【渝粤教育】电大中专市场营销管理 (2)作业 题库

1企业的内部环境不包括() A企业的生产能力 B财务能力 C社会文化环境 D企业在公众中的形象 错误 正确答案:左边查询 学生答案:A 2企业营销战略规划不包括哪一步骤() A确定企业的任务与目标 B选择合宜的市场机…

pandas 遍历并修改_Pandas循环提速7万多倍!Python数据分析攻略

乾明 编译整理 量子位 报道 | 公众号 QbitAI用Python和Pandas进行数据分析,很快就会用到循环。但在这其中,就算是较小的DataFrame,使用标准循环也比较耗时。遇到较大的DataFrame时,需要的时间会更长,会让人更加头疼。现…

spring消息队列_AmazonSQS和Spring用于消息传递队列

spring消息队列下一篇文章将演示如何将Spring JMS模板和DLMC与AmazonSQS API一起使用,以放置消息队列。 我为什么要使用Amazon SQS? 易于配置 跨平台支持 从您的自我冗余,连带和扩展方面的烦恼中赚钱。 为什么我不使用Amazon SQS&#x…

教程:用Java创建和验证JWT

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。 Java对JWT(JSON Web令牌)的支持过去需要进行大量工作&#xf…

python用递归法写斐波那契_python实现斐波那契数列: 递归+备忘录法+动态规划实现...

1.为什么备忘录法和动态规划法:斐波那契是很多人入门递归思想的第一课,所以很多人都会最简单的一种递归写法,但是其实递归的过程,他的时间复杂度非常高,达到了O(2的n次方)这样的一个指数级别。先看最简单的&#xff1a…

【渝粤教育】电大中专电商运营实操 (2)_1作业 题库

1.电子商务最重要的是() A.商务 B.网站 C.货物 D.信息技术 正确 正确答案:左边查询 学生答案:A 2.目前菜鸟网络依赖大数据和云计算已实现了哪些功能() A.自动化仓库 B.智能发货 C.物流云加速 D.以上都正确 …

q7goodies事例_Java 8 Friday Goodies:java.io终于成功了!

q7goodies事例在Data Geekery ,我们喜欢Java。 而且,由于我们真的很喜欢jOOQ的流畅的API和查询DSL ,我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 我们已经写了一些关于Java 8好东西的博客 ,现在我们觉得是时候开始一个…

python人脸识别环境搭建_Win10:Python3.6安装face_recognition人脸识别库

face_recognition简介face_recognition是Python的一个开源人脸识别库,支持Python 3.3和Python 2.7。引用官网介绍:Recognize and manipulate faces from Python or from the command line with the worlds simplest face recognition library.安装配置我…

【渝粤教育】电大中专电子商务网站建设与维护 (11)作业 题库

1.目前,阿里巴巴集团旗下主要交易市场不包括哪个( ) A.中国批发交易平台 B.全球批发交易平台 C.中国交易市场 D.国际交易市场 错误 正确答案:左边查询 学生答案:未作答 2.阿里巴巴是于1999年创立的( &#…

【渝粤教育】电大中专电子商务网站建设与维护 (9)作业 题库

1.阿里巴巴的创始人是( ) A.丁磊 B.马云 C.马化腾 D.李彦宏 错误 正确答案:左边查询 学生答案:未作答 2.阿里巴巴是于1999年创立的( )的网上贸易市场平台。 A.企业对企业 B.零售商对消费者 C.企业对零售商 …

AWS re:Invent 2018的5大公告

AWS re:Invent刚刚完成。 这是一个巨大的活动,在拉斯维加斯7家最大的酒店中,有50,000多名与会者,并发布了许多新服务。 无服务器通过新的lambda增强功能和更好的容器支持而继续引起人们的广泛关注。 AWS通过新的“ Outposts”功能…

【渝粤教育】电大中专药事管理与法规作业 题库

1.根据《执业药师职业资格制度规定》,取得药学类相关专业大专学历,报考执业药师考试,要求在药学或中药学岗位工作的年限为( )。 A.5年 B.4年 C.3年 D.6年 错误 正确答案:左边查询 学生答案:A 2.…