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

输出:无法兑现

相关推荐
tedcloud12333 分钟前
RTK部署教程:构建稳定的AI Workflow环境
服务器·javascript·人工智能·typescript·ocr
ZC跨境爬虫1 小时前
跟着 MDN 学CSS day_16:(深入掌握背景与边框的艺术)
前端·css·ui·html·tensorflow
道里4 小时前
花了 5 万刀用 AI 写代码之后,这是我的全部经验
前端·人工智能
Royzst4 小时前
xml知识点
java·服务器·前端
IT_陈寒5 小时前
React useEffect闭包陷阱差点把我整失业了
前端·人工智能·后端
kyriewen5 小时前
推行AI写代码一年后,Code Review变成了新的加班理由
前端·ai编程·cursor
前端环境观察室6 小时前
给 Agent Browser Workflow 加一层可观测性:Trace、Snapshot 和 Review Queue
前端
柒瑞6 小时前
Superpowers结合Claude code浅实战
前端
Nian.Baikal6 小时前
从零搭建离线地图服务:Nginx + Cesium/Leaflet 实战指南
运维·前端·nginx
zithern_juejin6 小时前
new 运算符
javascript