过一遍|Promise面试题(会持续补充的)

2023-05-16

Promise一直是面试被问的重点,根据各种面经记录Promise在面试中经常问道的问题,同时会增加一些前瞻性和拓展性的问题,适合我这种临时抱佛脚的人

前期储备

事件循环(event loop)的执行顺序

详见B栈哈默:什么是Event Loop?
如果用比较文字的形式来解释:

  1. 一开始整个脚本作为宏任务执行
  2. 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  3. 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完毕
  4. 执行浏览器UI线程的渲染工作
  5. 检查是否有Web worker任务,有则执行
  6. 执行完本轮的宏任务,回到2,依次循环,直到宏任务和微任务队列都为空
    微任务包括:MutationObserverPromise.then()catch()Promise为基础开发的其他技术,比如fetch APIV8垃圾回收过程、Node独有的process.nextTick()
    宏任务包括:scriptsetTimeoutsetIntervalsetImmediateI/OUI rednering

注意:在所有任务开始的时候,由于宏任务中包括了script,所以浏览器会先执行一个宏任务,在这个过程中你看到的延迟任务(例如setTimeout)将被放到下一轮

Promise常考面试题(问答)

(1)简单说一下Promise,谈谈你对Promise的理解

个人遇到这样的问题会从以下几个方面回答: 【基础回答】Promise是什么?有什么作用?解决了什么问题?【进阶回答】Promise相关的一些知识点(这样也可以让面试官顺藤摸瓜,沿着你的回答提问)

所以我会这样回答:
PromiseES6新增的语法,是一种异步编程的一种解决方案,Promise本质上是一个绑定了回调的对象。 Promise在一定程度上解决了回调函数的书写结构问题,解决了回调地狱的问题。Promise可以看作是一个状态机,它有三种状态:pendingfulfilledrejected,其中初始状态是pending,可以通过函数resolve(表示成功)把状态变为fulfilled,或者通过函数reject(表示失败)把状态变为rejected状态一经改变就不能再次变化

拓展问1: Promise有几种状态?

  • pending
  • fulfilled
  • rejected

拓展问2: Promise的状态可变吗?

状态不可变

const p = new Promise((resolve, reject) => {
    resolve(1);
    reject(2);
})

p.then(data => {
    console.log(data)
}).catch(err => {
    console.log(err);
})


// 1  已经进入resolved状态,不会进到rejected

拓展问3:什么是回调地狱?

什么是回调地狱? 答: 回调地狱就是嵌套回调问题,具体来说就是异步返回值又依赖于另一个异步返回值(看下面的代码)

function double(value, success, failure) {
	setTimeout(()=>{
		try {
			if (typeof value !== 'number') {
				throw 'Must provide number as first argument';
			}
			success(2*value);
		} catch(e) {
			failure(e);
		}
	},1000);
}

const successCallback = (x) => {
	double(x, (y) => console.log(`Success:${y}`));
};

const failureCallback = (e) => console.log(`Failure:${e}`);

double(3, successCallback, failureCallback);

// Success: 12 (大约1000毫秒以后)

/*
	显然,随着代码越来越复杂,回调策略是不具有扩展性的
*/

拓展问4: Promise是如何解决地狱回调的?

看下面这个例子:

function request1() {
    return new Promise(resolve => resolve(1));
};

function request2() {
    return new Promise(resolve => resolve(2));
};

function request3() {
    return new Promise(resolve => resolve(3));
};

request1()
    .then(data => {
        console.log('1data',data)
        return request2();
    })
    .then(data => {
        console.log('2data',data);
        return request3();
    });

then里面可以return Promise,来防止地狱回调

(2)你用过Promise吗?为什么要用?

(3)promise的方法有哪些?Promise的API了解多少?

prmoise.all()

Promise.all([]).then(res=> {}).catch(err => {})

该方法接收一个可迭代对象(如Array,Map,Set),返回一个Promise(新期约),只有当该数组中所有的Promise完成后才会有pending转变为resolved执行then里面的回调函数;若数组中有任意一个promise被拒绝则会执行失败回调,即catch方法会捕获到首个被执行的reject函数。通过该方法获得的成功结果的数组里面的数据顺序和接收到的promise数组顺序是一致的。

Promise.race()

Promise.race([]).then(res => {}).catch(err => {})

Promise.race()Promise.all()一样都是将多个Promise实例组合成一个Promise的静态方法
不同的是:Promise.race()当传入的数组中有任意一个promise被拒绝或者成功,则会采用第一个promise作为返回值,若成功则执行then,失败执行catch

Promise.any()

区别于Promise.all(), Promise.any() 只要有一个成功,就返回成功的promise,如果没有一个成功,就返回一个失败的promise

Promise.any([]).then(res => {}).catch(err => {})

Promise.allSelected()

Promise.allSelected([]).then(res => {}).catch(err => {})

返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果

看文档的例子:

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));

// expected output:
// "fulfilled"
// "rejected"

(4)async await 和Promise

拓展1:执行async函数,返回的都是Promise对象

看下面这个例子:

async function test1() {
    return 1;
};

async function test2() {
    return Promise.resolve(1);
};


const res1 = test1();
const res2 = test2();

console.log('res1', res1);
console.log('res2', res2);

在这里插入图片描述
根据打印结果,我们可以发现,如果使用async执行函数,返回一个基本数据类型,会包装成Promise对象

拓展2:Promise.then 成功的情况,对应的是await

async function test3() {
    const p3 = Promise.resolve(3);

    p3.then(data => {
        console.log('data',data);
    });

    const data = await p3;
    console.log(data);
}

test3();

在这里插入图片描述
我们再看另一种情况:

async function test3() {
    const data = await 4;
    console.log('data',data);
}

test3();

在这里插入图片描述
其实await帮助我们把后面的4封装成一个Promise对象
如果我们改一下,改成reject和catch

async function test3() {
    const p3 = Promise.reject(3);

    p3.catch(data => {
        console.log('data',data);
    });

    const data = await p3;
    console.log('data',data);
}

test3();

在这里插入图片描述
看到3被打印出来了,但是异常并没有捕获到

拓展3:Promise.catch 异常的情况, 对于try…catch…

我们把上面没有捕获到异常的代码进行改写:

async function test3() {
    const p3 = Promise.reject(3);

    p3.catch(data => {
        console.log('data',data);
    });

    try {
        const data = await p3;
        console.log('data',data);
    } catch (err) {
        console.log('err', err);
    }
    
}

test3();

在这里插入图片描述

拓展4: 如何让Promise顺序执行?应用场景是什么?

async await

看下面这个例子:

const p1 = new Promise(resolve => {
    setTimeout(()=> {
        resolve(1);
    }, 3000);
});


const p2 = new Promise(resolve => {
    setTimeout(()=> {
        resolve(2);
    }, 2000);
});

const p3 = new Promise(resolve => {
    setTimeout(()=> {
        resolve(3);
    }, 1000);
});

async function execute() {
    await p1.then(data => console.log(data));
    await p2.then(data => console.log(data));
    await p3.then(data => console.log(data));
};

execute();

Promise常考输入输出题(待补充)

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

过一遍|Promise面试题(会持续补充的) 的相关文章

随机推荐