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

输出:无法兑现

相关推荐
风骏时光牛马13 小时前
AI多模块协同自动化工作流
前端
zhangminghuan13 小时前
前端面试应对技巧
前端·面试·职场和发展
计算机魔术师13 小时前
潜伏14年的PHP致命洞,AI跑两天就交卷了
前端
IT_陈寒13 小时前
Vue的这个响应式陷阱我居然现在才发现
前端·人工智能·后端
Python自动化直播14 小时前
智能脚本批量管理拆开看:导入、导出、重写、试听
前端·python·ai·秒播
开开心心就好14 小时前
发票信息录入太麻烦?离线工具一键转表格
运维·服务器·前端·pdf·ocr·maven·intellij-idea
এ慕ོ冬℘゜15 小时前
ES Promise.all/ Promise.race
前端·javascript·elasticsearch
喜欢打篮球的普通人16 小时前
LLVM 前端与优化层:Clang 如何把 C 代码变成 IR
c语言·前端·状态模式
只睡四小时16 小时前
端侧推理:face-api.js 首屏 92% 是白下载的
开发语言·javascript·人脸识别·ecmascript·tensorflow.js·face-api.js·端侧推理