每日一题 线程池与Promise

64 阅读2分钟

线程池

在B站上看的一道题,看了题意后,进行了尝试。(之前也做过相似的题,最近面试也被考过一道类似的题目,当时差点没做出来。幸好面试官当时要我做的是另一个意思的。所以这个题就直接顺手写下

B站视频link

class Scheduler {
    _current = 0
    _pendingTasks: (() => Promise<any>)[] = []
    _runningTask: Promise<any> | null = null
    run() {
        // 如果有任务并且还有坑位
        if (this._current < this.num && this._pendingTasks.length > 0) {
            this._current++
            this._pendingTasks[0]().then(() => {
                this._current--;
                // 启动下一个任务
                this.run()
            })
            this._pendingTasks = this._pendingTasks.slice(1, this._pendingTasks.length)
        }
        // 等待某些任务执行完去启动。
    }
    addTask(task: () => Promise<any>) {
        this._pendingTasks.push(task)
        // 启动!
        this.run()
    }

    constructor(private num: number) {}
}

const getTime = () => {
    const date = new Date()
    return date.valueOf() / 1000
}
const start = getTime()

const createPromise = (time: number, desc: string) => new Promise<void>((resolve, reject) => {
    console.log(`${desc} is start running ${getTime() - start}`)
    setTimeout(() => {
        resolve()
        console.log(`${desc} fulfilled ${getTime() - start}`)
    }, time)
})
const scehduler = new Scheduler(2)

scehduler.addTask(() => createPromise(1000, "first"))
scehduler.addTask(() => createPromise(2000, "second"))
scehduler.addTask(() => createPromise(3000, "third"))
scehduler.addTask(() => createPromise(4000, "forth"))

可以看到相应的 输出符合预期

面试时

当时面试官让我做的是

easy mode

有一个数组 ,有一个 async 函数 返回参数 a+b 的值,然后不能使用 + ,求数组和。那么很简单了

const add = async (a: number, b: number) => Promise.resolve(a + b)

const sum = async (arr: number[]) => {
    let res = 0
    for (let i = 0; i < arr.length; i++) {
        res = await add(res, arr[i])
    }
    return res
}
sum([1,2,3,4]).then(console.log)

(牛客网死活输出不了数据,我猜测是牛客网宏任务运行完了就认为任务结束了,但是这个输出是在微任务里)

a little harder

有一个数组,还是求和,需要并行计算,减少计算时间。

我一开始还以为是线程池,然后还想了一会儿,但是线程池每个任务都是独立的,这个还是有一定依赖关系,有时候还会有一些问题,比如如下代码,可能会有问题

res=res+await add(a,b)
// 应写为
const _temp=await add(a,b)
res=res+_temp

上述代码如果有多个并行任务,取值的时候和更新的时候就不同步,就会计算错误。 后来经过面试官解释,大概明白什么意思了,这个还没有涉及到线程池,就是单纯的分组求和

const sum = async (arr: number[]) => {
    if (arr.length === 1) {
        return arr[0]
    }
    const promises = []
    const set = Math.floor(arr.length / 2)
    for (let i = 0; i < set; i++) {
        promises.push(add(arr[2 * i], arr[2 * i + 1]))
    }
    if (arr.length - 1 === set * 2) {
        promises.push(Promise.resolve(arr[arr.length - 1]))
    }
    const res = await Promise.all(promises)
    return sum(res)
}

大概就是两两分组,每一组都同时进行,如果是奇数也放到结果的数组里,每一次递归将数组长度减小一半,最后获得结果。