前端八股整理(手写 01)|Promise 超时控制、红绿灯与 Promise.all

文章目录

  • [前端八股整理(手写 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);
});

输出:无法兑现

相关推荐
万少11 小时前
Vibe Coding不停歇,移动端 TRAE SOLO 让你用手机也能编程啦
前端·javascript·后端
kyriewen1111 小时前
WebAssembly:前端界的“外挂”,让C++代码在浏览器里跑起来
开发语言·前端·javascript·c++·单元测试·ecmascript
烛衔溟12 小时前
TypeScript 接口的基本使用 —— 定义对象形状
前端·javascript·typescript
铁皮饭盒13 小时前
成为AI全栈 - 第3课:路由 RESTful Elysia 状态码 设计规范
前端·后端·全栈
顾昂_13 小时前
Web 性能优化完全指南
前端·面试·性能优化
IT乐手13 小时前
Claude Code + Qwen 的配置方法
javascript·claude
前端程序媛-Tian13 小时前
前端 AI 提效实战:从 0 到 1 打造团队专属 AI 代码评审工具
前端·人工智能·ai
支付宝体验科技13 小时前
Ant Design Pro v6.0.0 发布
前端
T畅N14 小时前
审批流设计器(前端)
前端·elementui·vue·html·流程图·js