Promise面试相关题目总结

43 阅读3分钟

Promise相关实现

请求三秒,超时就中断请求

function delay(t) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(`超出3秒了,您已经请求超时`)
        }, t)
    })
}
​
function p2() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('xxx')
        }, 4000)
    })
}
​
function timeOutWrapper() {
    Promise.race([delay(3000), p2()]).then(res => {
        console.log(res)
    }).catch(e => {
        console.log(e)
    })
}
​
timeOutWrapper()

JS异步数据流,实现并发异步请求,结果顺序输

// JS异步数据流,实现并发异步请求,结果顺序输 // promise.all([])顺序输出结果
const asyncRequest = (url) => new Promise((resolve) => {
    setTimeout(() => {
        console.log('请求成功了', url)
        resolve(`成功响应: ${url}`);
    }, Math.random() * 1000)
})
​
//并发的URL数组
const urls = ['url1', 'url2', 'url3', 'url4', 'url5']
​
// 异步请求数组
const promises = urls.map(url => asyncRequest(url))
​
Promise.all(promises).then(results => {
    console.log("所有请求完成,按照顺序输出结果:", results)
}).catch(error => {
    console.log("请求出错:", error)
})

promise中断请求

// promise中断请求
class AbortablePromise {
    construct(promise) {
        this.promise = promise;
        this.abort = false
    }
​
    abort() {
        this.abort = true
    }
​
    then(resolve, reject) {
        if (this.abort) {
            reject(new Error("Promise  aborted"))
        } else {
            return this.promise.then(resolve, reject)
        }
    }
​
​
    catch(reject) {
        if (this.abort) {
            reject(new Promise("Promise aborted"))
        } else {
            return this.promise.catch(reject)
        }
    }
​
    finally(onFiinally) {
        return this.promise.finally(onFiinally)
    }
}
​
const delay = (ms) => new Promise((resolve => {
    setTimeout(resolve, ms)
}))
​
const abortAbleDelay = new AbortablePromise(delay(3000).then(() => {
    console.log("Promise resolved")
}))
​
//模拟中断请求
abortAbleDelay.abort()
abortAbleDelay.then(() => {
    console.log('promise fullfilled')
}, (error) => {
    console.log('promise rejected', error.message)
})

setTimeout 补偿系统时间

// setTimeout 补偿系统时间
function mySetTimeout(fn, delay, ...args) {
    let currentTime = Date.now()
    let timer = null
    const task = () => {
        currentTime += delay
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, currentTime - Date.now())
    }
​
    task()
    return clearTimeout(timer)
}
​
// 使用高精度时间戳,减少setTimout倒计时误差
function countDown(duration, callback) {
    const startTime = performance.now()
    function tick() {
        const currentTime = performance.now()
        const elapsed = currentTime - startTime
        const remaining = duration - elapsed
        if (remaining <= 0) {
            callback()
        } else {
            setTimeout(tick, Math.max(0, remaining))
        }
    }
​
    tick()
}
​
const duration = 6000
countDown(duration, () => {
    console.log('倒计时结束')
})

promise 串行

/**
 * 在Promise中,当后续的Promise需要用到之前的Promise处理结果时,需要Promise的串联,无论是then还是catch方法,他们都具有返回值, 返回的是一个全新的Promise对象。
 * 他的状态满足以下规则:
 *  如当前的Promise是未决的,得到的新Promise是挂起的状态
 * 如果当前的Promise是已决的,会运行响应后的处理函数,并将后续处理函数的处理结果(返回值)作为resolved的状态数据,应用得到的新的Promise对象,如果后续处理函数发生错误,则把返回值
 *  作为rejected的状态数据,应用到新的Promise对象。
 *  后续的Promise 一定会等到前面的Promise有了处理结果后才会变为已决状态
 * @param promises
 */

//async await

async function executeSerial(promises) {
    for (let p of promises) {
        let res = await p
        console.log(res)
    }
}

let p1 = new Promise(resolve => setTimeout(() => resolve('Promise 1 resolved'), 1000))
let p2 = new Promise(resolve => setTimeout(() => resolve('Promise 2 resolved'), 2000))
let p3 = new Promise(resolve => setTimeout(() => resolve('Promise 3 resolved'), 1000))

const promises = [p1, p2, p3];

executeSerial(promises).then(res => {
    console.log('All promises resolved sequentially')
})


// reduce

function executeSerial2(promises) {
    return promises.reduce((p, c) => {
        return p.then(c)
    }, Promise.resolve())
}

let resArr = executeSerial2(([p1, p2, p3]))
resArr.then(res => {
    console.log(res)
})


// 3. forEach
function promiseSerial(promises) {
    let res = Promise.resolve()
    promises.forEach(p => {
        res = res.then(() => p)
    })
}

const promises = new Array(4).fill().map((item, index) => {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(index);
            resolve(index);
        }, index * 1000)
    })
})
promiseSerial(promises)

const p1 = new Promise((resolve, reject) => {
    resolve(123) //123 给p1 就是已决议状态
})

//123*2 返回状态就是已决议
const p2 = p1.then(res => res * 2)
p2.then(res => {
    console.log(res) // 输出456就是已决状态
})

//reject
let pr = new Promise((resolve, reject) => {
    throw 1 //推向rejected
})

const p3 = pr.then(res => {
    // 这里的pro2先挂起,登Pro2先为挂起,pr.then()
    return res * 2
}, err => {
    return err * 3 //推向已决
})
p3.then(res => {
    console.log(res)
})

处理高并发, 100 条数据,带宽为 10, 跑满带宽

// 创建任务
    function task(){
        return new Promise(resolve =>{
            setTimeout(()=>{
                resolve(+new Date())
            } ,1000)
        })
    }
​
    // 并发请求方法
    class  asyncProcessQueue {
        constructor(max) {
            this.poolList = new Set()
            this.max = max || 10
            this.tasks = []
            this.results =[]
        }
​
        addTask(task) {
            this.tasks.push(task)
        }
​
        async runTask() {
            for (const task of this.tasks) {
                if(this.poolList.size  === this.max) {
                    await Promise.race(this.poolList).catch(err => console.log(err,888))
                }
                // 去除promise
                const p = task()
                const removeTask = ()=> this.poolList.delete(p)
                // 在Promise.race中,被执行完毕,就会被remove掉,然后接下来再push,不管这个promise被resolved, rejected,都是会被执行掉的
                p.then(removeTask,removeTask)
                this.poolList.add(p)
                this.results.push(p)
            }
            return Promise.allSettled(this.results)
        }
    }
​
    let apq = new asyncProcessQueue(10)
    for (let i = 0; i <12; i++) {
        apq.addTask(task)
    }
    apq.runTask().then(res=>{
        console.log(res)
    })

设计一个简单的任务队列, 要求分别在 1,3,4 秒后打印出 "1", "2", "3"

//设计一个简单的任务队列, 要求分别在 1,3,4 秒后打印出 "1", "2", "3";
    class TaskQueue{
        constructor() {
            this.tasks = []
            this.currentTask = 0
        }
        // 添加任务
        addTask(task,delay) {
            this.tasks.push({task,delay})
        }
​
        start() {
            this.runTask()
        }
​
        async runTask() {
            // 判断是否还有任务
            if(this.currentTask <this.tasks.length) {
                // 任务队列取出任务
                const {task,delay} = this.tasks[this.currentTask]
                //用于实现延迟功能
                await this.delay(delay)
                task() //执行任务
                this.currentTask++
                //执行完任务继续执行下一个任务
                this.runTask()
            }
        }
​
        //创建delay函数
​
        delay(seconds) {
            return new Promise(resolve => setTimeout(resolve,seconds*1000))
        }
    }
​
    const taskQueue = new TaskQueue();
​
    // 添加任务
    taskQueue.addTask(() => console.log("1"), 1);
    taskQueue.addTask(() => console.log("2"), 3);
    taskQueue.addTask(() => console.log("3"), 4);
​
    // 启动任务队列
    taskQueue.start();

请求五秒未完成就停止

function delay(time) {
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log(`超出时${time}秒`)
            reject('超出时'+time+'秒')
        }, time)
    })
}
​
const p2 = new Promise((resolve)=>{
     setTimeout(()=>{
         console.log('这是正常的请求')
         resolve('这是正常的请求')
     },6000)
})
​
function timeOutWrapper(arr) {
   Promise.race(arr).then(res=>{
       console.log(log(res))
   }).catch(err=>{
       console.log(err)
   })
}
​
timeOutWrapper([p2, delay(3000)])

实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个

// 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
class Scheduler {
    constructor() {
        this.queue = [];
        this.maxNum = 2;
        this.runningNum = 0;
    }
​
    addTask(time, order) {
        const promiseGenerator = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(order)
                    resolve(order)
                }, time)
            })
        }
        this.queue.push(promiseGenerator)
    }
​
    start() {
        for (let i = 0; i < this.maxNum; i++) {
            this.run()
        }
    }
​
    run() {
        if (this.runningNum <= this.maxNum && this.queue && this.queue.length > 0) {
            this.queue.shift()().then(res => {
                console.log(res)
                this.runningNum--
                this.run()
            })
            this.runningNum++
        }
    }
}
​
const scheduler = new Scheduler()
const addTask = (time, order) => {
    scheduler.addTask(time, order)
}
​
for (let i = 1; i < 8; i++) {
     addTask(i*1000, i)
}
scheduler.start()

并发控制async-pool

// async-pool
//function asyncPool(poolLimit, array, iteratorFn){ ... }
// poolLimit(数字类型):表示限制的并发数;
// array(数组类型):表示任务数组;
// iteratorFn(函数类型):表示迭代函数,用于实现对每个任务项进行处理,该函数会返回一个 Promise 对象或异步函数。//es7
async function asyncPool(poolLimit, array, iteratorFn) {
​
    const allTasks = []; // 存储所有的异步任务
    const currentTasks = []; // 存储正在执行的异步任务
    for (const item of array) {
        // 创建一个 Promise,用于在所有任务完成后再运行
        const p = Promise.resolve().then(() => iteratorFn(item, array));
        allTasks.push(p); // 保存新的 Promise
        // 当pooLimit <= array.length时,表示当前运行的异步任务数量达到限制,需要等待部分任务完成, 进行并发控制
        if (poolLimit <= array.length) {
            // 当前任务完成后,从正常的执行任务的数组中,移除已经完成的任务
            const runningTask = p.then(() => currentTasks.splice(currentTasks.indexOf(runningTask), 1));
            // 保存正常执行的异步任务
            currentTasks.push(runningTask);
            if (currentTasks.length >= poolLimit) {
                await Promise.race(currentTasks).then(res => console.log(typeof res)); // 等待较快的任务完成
            }
        }
    }
    // 等待所有任务完成
    return Promise.all(allTasks);
}
​
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
​
let limit = 3
let array = [1000, 2000, 3000, 4000, 5000, 6000, 7000]
asyncPool(limit, array, timeout).then(res => console.log(res))
​
​
//es6 实现async-pool
function asyncPool(poolLimit, array, iteratorFn) {
    let i = 0
    const allTasks = []
    const currentTask = []
    const enqueue = function () {
        if (i === array.length) {
            return Promise.resolve()
        }
        //获取新的任务
        const item = array[i++]
        const p = Promise.resolve().then(res => iteratorFn(item, i))
        allTasks.push(p)
        let r = Promise.resolve()
        if (poolLimit <= array.length) {
            const runTask = p.then(() => currentTask.splice(currentTask.indexOf(p), 1))
            currentTask.push(runTask)
            if (currentTask.length >= poolLimit) {
                r = Promise.race(currentTask)
            }
        }
        // 正在执行任务列表 中较快的任务执行完成之后,才会从array数组中获取新的待办任务
        return r.then(()=>enqueue())
    }
    return enqueue().then(()=>Promise.all(allTasks))
}

使用 Promise 实现每隔三秒输出时间

//使用 Promise 实现每隔三秒输出时间
function timeTask(time){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            console.log(new Date())
            resolve()
        },time)
    })
}
​
const taskRunner = async() =>{
    await timeTask(3000)
    taskRunner()
}
taskRunner()

Promise.retry 超时重新请求,并在重试一定次数依然失败时输出缓存

// Promise.retry 超时重新请求,并在重试一定次数依然失败时输出缓存const delay = (timeout) => new Promise(resolve => {
    setTimeout(resolve, timeout)
})
​
async function retry(fn, maxAttempts, timeout, cache) {
    let count = 0
    while (count < maxAttempts) {
        try {
            return await Promise.race([fn, delay(timeout)])
        } catch (e) {
            count++
            console.log(`Attempt ${count} failed!${e}`)
            if (maxAttempts === count) {
                return cache
            }
        }
    }
}
​
const num = 3
const timeOut = 3
const cache = 'Cache'let p = Promise.reject(new Error('Request failed'))
// let p = Promise.resolve('88888')
​
​
retry(p, num, timeOut, cache).then(data => {
    console.log(data, 666)
}).catch(e => {
    console.log(e, 555)
})

每隔1秒打印12345

//let + 闭包
for (let i = 1; i <= 5; i++){
  setTimeout(()=>console.log(i),i*1000)
}
const delay = (timeout) => new Promise(resolve => {
    setTimeout(() => {
        resolve(timeout / 1000)
    }, 1000)
})
​
async function generate() {
    for (let i = 1; i < 6; i++) {
        let res = await delay(i * 1000)
        console.log(res)
    }
}
​
generate()