Promise题解

91 阅读3分钟

题目

class Scheduler {
  add(promiseCreator) { ... }
  // ...
}
const timeout = (time) => new Promise(resolve => {
  setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
  scheduler.add(() => timeout(time))
    .then(() => console.log(order))
}

addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')// output: 2 3 1 4
// 一开始,1、2两个任务进入队列
// 500ms时,2完成,输出2,任务3进队
// 800ms时,3完成,输出3,任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4

先看add 这个函数,说明这个函数返回的是个promise,先写一个基本的返回promise的return

add() {
    return new Promise((resolve, reject) => {
    })
}

看这个addTask这个函数的返回,说明这就是经典promise题 最多并发数为x的问题,这道题的并发数为2。 看这个函数的输出结果是通过then链console出来的,输出值和add函数没有关系,add函数只需要里面的时间函数的status变为fulfilled时将自己的status也变成fulfilled即可
并发数的解题思路就是
1.建立一个任务队列,每次调用方法就将任务push进队列
2.建立一个执行队列,每次调用方法时都会查询这个队列中正在执行的任务是不是大于max个。如果大于max个,就将任务push进队列,并执行
3.每当任务执行完就从任务队列中推出一个任务,并把他push进执行队列并执行

const taskList = []
const runList = []
add(task){
    taskList.push(task)
    if(runList.length<2) {
        const runTask = taskList.shift()
        runList.push(runTask)
        runTask().then(res => {
            runList.remove(runTask) // 先写伪代码,具体实现可以后写
            // 代补充 -- 处理resolve相关的逻辑去修改 promise的状态
            if(taskList.length) {
               const newTask = taskList.shift()
               runList.push(newTask)
               newTask.then(...)
            }
        })
    }
}

发现出现了重复的逻辑,考虑可以把这块相同的代码这个抽成一个函数递归调用

const taskList = []
const runList = []
add(task) {
    taskList.push(task)
    if(runList.length<2) {
        function f1() {
            if(taskList.length) {
                const runTask = taskList.shift()
                runList.push(runTask)
                runTask().then(res => {
                    runList.remove(runTask)
                    // 代补充 resolve 的逻辑
                    f1()
                })
            }
        }
        f1()
    }
}

将这段逻辑放到之前写的return new Promise中,并试着resolve的逻辑

const taskList = []
const runList = []
add(task) {
    return new Promise((resolve, reject) => {
        taskList.push(task)
        if(runList.length<2) {
            function f1() {
                if(taskList.length) {
                    const runTask = taskList.shift()
                    runList.push(runTask)
                    runTask().then(res => {
                        runList.remove(runTask)
                        resolve()
                        f1()
                    })
                }
            }
            f1()
        }
    })
}

发现这么写resolve不对,递归的时候会出现问题,每次递归的resolve都用的是一个resolve。想想每个task都应该它的resolve绑定。那就要更改一下将任务推到任务队列时的逻辑

add(task) {
    return new Promise((resolve, reject) => {
        const newTask = {
            task,
            cb: (value) => {resolve(value)} // 因为这道题实现只需要改状态不需要传值。所以这里的value可传可不传
        }
        taskList.push(newTask)
        ...
        runTask.task().then(res => {
            runTask.cb(res)
        })
        
    })
}

整合一下, 并实现一下remove

class Scheduler{
    // 转换成类里面的私有属性
    taskList = []
    runList = []
    add(task) {
        return new Promise((resolve, reject) => {
            const newTask = {
                task,
                cb: (value) => {resolve(value)}
            }
            this.taskList.push(newTask)
            if(this.runList.length<2) {
                const f1 = () => { // 转换成类后这里要写成箭头函数,否则this指向会有问题
                    if(this.taskList.length) {
                        const runTask = this.taskList.shift()
                        this.runList.push(runTask)
                        runTask.task().then(res => {
                            // 实现remove逻辑
                            let index = this.runList.findIndex(val => val === runTask)
                            this.runList.splice(index, 1)
                            runTask.cb(res)
                            f1()
                        })
                    }
                }
                f1()
            }
        })
    }
}
const timeout = (time) => new Promise(resolve => {
  setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
  scheduler.add(() => timeout(time))
    .then(() => console.log(order))
}

addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')