文章目录
- [前端八股整理(手写 01)|Promise 超时控制、红绿灯与 Promise.all](#前端八股整理(手写 01)|Promise 超时控制、红绿灯与 Promise.all)
- [1.手写一个带超时控制的异步任务,就是假如说我现在有一个异步任务(它可能 1s,可能 5 秒会完成),给它限定一个2秒的时间,如果它超过这两秒,就直接返回一个超时的报错.](#1.手写一个带超时控制的异步任务,就是假如说我现在有一个异步任务(它可能 1s,可能 5 秒会完成),给它限定一个2秒的时间,如果它超过这两秒,就直接返回一个超时的报错.)
- [2.实现一个红绿灯的循环,比如说三秒之后 显示一个红灯,然后两秒之后显示一个绿灯,然后一秒后显示一个黄灯,然后这个让它就是按照这个顺序,红灯、绿灯、黄灯,然后让它一直循环。](#2.实现一个红绿灯的循环,比如说三秒之后 显示一个红灯,然后两秒之后显示一个绿灯,然后一秒后显示一个黄灯,然后这个让它就是按照这个顺序,红灯、绿灯、黄灯,然后让它一直循环。)
- 3.手写promise.all方法
前端八股整理(手写 01)|Promise 超时控制、红绿灯与 Promise.all
预备知识:
参考文章:MDN 文档:Web API setTimeout 方法
什么是 setTimeout 方法?
Window接口的 setTimeout() 方法设置一个定时器,一旦定时器到期,就会执行一个函数或指定的代码片段。
语法如下:
javascript
setTimeout(functionRef, delay)
参数:
-
functionRef:当定时器到期后要执行的函数。
-
delay(可选):定时器在执行指定的函数或代码之前应该等待的时间,单位是毫秒。如果省略该参数,则使用值 0,意味着"立即"执行,或者更准确地说,在下一个事件循环执行。
-
param1...param2(可选):会被传递给由
functionRef指定的函数的附加参数
返回值:
返回值 timeoutID 是一个正整数,表示由 setTimeout() 调用创建的定时器的标识符。可以将这个值传递给 clearTimeout() 来取消该定时器。
关于 this:
如果你没有在调用中或用 bind 设置 this,它将默认为 window
拓展:
如果要重复调用某个函数(如每 N 毫秒调用一次),考虑使用 setInterval()。
1.手写一个带超时控制的异步任务,就是假如说我现在有一个异步任务(它可能 1s,可能 5 秒会完成),给它限定一个2秒的时间,如果它超过这两秒,就直接返回一个超时的报错.
- 思路 1:手动控制整个流程
- 思路 2:利用 promise 的现成能力
思路 1:
让"原任务"和"超时任务"同时开始跑,谁先结束就听谁的。
创建一个超时控制的异步任务,在它的执行器函数中,写入一个setTimeout 函数,延迟设置为两秒,如果两秒后没反应,直接 reject,传入理由超出了 2 秒的时间限制,用.catch 方法打印这个错误,同样这个 taskpromise如果正常就返回他对应的 resolve 或者reject,但是注意在 then 和 catch 方法中清理掉计时器,或者也可以用finally 方法清理计时器.整体的代码:写在一个新的 promise 包装器中,这样更像手撕代码,传入 promise 和延迟 delay
参考代码:
javascript
function withTimeout(taskPromise,timeout=2000)
{
return new Promise((reslove,reject)=>{
// 执行器函数
const timer=setTimeout(()=>{
// 返回一个超时的错误,promise 变为 rejected 状态
reject(new Error("超出了时间限制"));
},timeout)
taskPromise.then((res)=>{
clearTimeout(timer);
reslove(res);
}).catch((err)=>{
clearTimeout(timer);
reject(err);
})
})
}
测试代码:
javascript
const task1=new Promise((reslove)=>{
setTimeout(()=>{
reslove("任务 1 成功");
},1000)
})
withTimeout(task1,2000).then((res)=>{
console.log(res);
}).catch((err)=>{console.log(err)});
javascript
const task2=new Promise((reslove)=>{
setTimeout(()=>{
reslove("任务 2 成功");
},5000)
})
withTimeout(task2,2000).then((res)=>{
console.log(res);
}).catch((err)=>{console.log(err.message)});
整体输出
bash
任务 1 成功
超出了时间限制
思路 2:
用.race 方法,返回一个执行结束的 promise 对象
javascript
function withTimeout(taskPromise,timeout=2000)
{
let timer;
const timeoutPromise=new Promise((_,reject)=>{
timer=setTimeout(()=>{
reject("超过了时间限制")
},timeout)
})
return Promise.race([taskPromise,timeoutPromise]).finally(()=>{
clearTimeout(timeout);
})
}
测试同思路 1
2.实现一个红绿灯的循环,比如说三秒之后 显示一个红灯,然后两秒之后显示一个绿灯,然后一秒后显示一个黄灯,然后这个让它就是按照这个顺序,红灯、绿灯、黄灯,然后让它一直循环。
思路:我会先封装一个
sleep函数,用 Promise 包一层setTimeout。然后在async function里面用while(true)表示无限循环,每一步通过await sleep()控制等待时间。这样可以保证红灯、绿灯、黄灯按顺序串行执行,也不会像setInterval那样出现上一轮还没执行完、下一轮又开始的问题。
javascript
function sleep(delay)
{
return new Promise (reslove=>{
setTimeout(reslove,delay);
})
}
async function trafficLight () {
while(true)
{
await sleep(3000);
console.log("红灯");
await sleep(2000);
console.log("绿灯");
await sleep(1000);
console.log("黄灯");
}
}
trafficLight();
3.手写promise.all方法
首先明确 promise.all 方法是什么?
Promise.all() 是Promise 的一个静态方法,接收一个可迭代对象,通常是一个 Promise 数组,返回一个新的 Promise。Promise.all([p1, p2, p3])
这个静态方法会在所有的 promise 全部解决之后再解决.
如果都被成功解决,返回一个 promise 的兑现值就是包含所有 promise 兑现值的数组,按照迭代器顺序.
如果有一个promise 待定,则返回的这个promise,也会是待定
如果有一个 promise 拒绝,返回的 promise 也会是拒绝,拒绝理由就是第一个拒绝 promise 的理由
思路:
参数:一个数组,数组包含了各种数或者 promise 对象
首先判断入参是否是数组,如果不是返回一个错误
创建一个结果数组,保存正确的结果
从数组中拿到每一个数,来遍历,先把他们用 resolve 方法包装一下,值变成 promise,promise 被包转返回他本身,然后.then 方法,将结果加入数组中,如果数目达到了本身入参数组的长度则成功返回结果,如果.catch 捕获错误,则直接返回 rejected,把这个返回的理由也返回.
javascript
// 传一个数组
function promiseAll(promises)
{
if(!Array.isArray(promises))
{
return new Promise((_,reject)=>{reject("参数必须为一个数组")});
}
if(promises.length===0) return Promise.reslove([]);
//核心代码
let result=[];
let resloveCounter=0;
return new Promise((reslove,reject)=>{
promises.forEach((promise,index)=>{
Promise.resolve(promise).then((res)=>{
result[index]=res;
resloveCounter++;
if(resloveCounter===promises.length) reslove(result);
}).catch((err)=>{
reject(err);
})
})
})
}
javascript
promiseAll([
2,
Promise.resolve(9),
new Promise(reslove=>{setTimeout(()=>reslove(60),3000);})
]).then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err);
});
输出:[ 2, 9, 60 ]
javascript
promiseAll([
2,
new Promise((_,reject)=>{setTimeout(()=>reject("无法兑现"),3000);}),
Promise.resolve(9)
]).then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err);
});
输出:无法兑现