1.题目如下
const request1 = () => new Promise(resolve => setTimeout(() => resolve(1), 4000))
const request2 = () => new Promise(resolve => setTimeout(() => resolve(2), 1000))
const request3 = () => new Promise(resolve => setTimeout(() => resolve(3), 2000))
const request4 = () => new Promise(resolve => setTimeout(() => resolve(4), 1500))
cosnt scheduler = max => {
// todo
}
const s = scheduler(2)
s(request1).then(res => console.log(res))
s(request2).then(res => console.log(res))
s(request3).then(res => console.log(res))
s(request4).then(res => console.log(res))
// 打印顺序如下
// 2, 3, 1, 4
2. 题目思考
额,果不其然,和去年面试字节的时候一样,字节还是喜欢搞这种题目。那就分析一下,max是每次运行最大的任务的总数,所以刚开始是request1和request2在执行状态,然后1000ms之后,request2执行了,打印2,然后request3进入执行状态,因为request3需要执行3000ms,所以先于request1执行,打印3,然后request4进入执行状态,过了1000ms``request1执行完毕,输出1,最后输出4.
s是一个函数,所以scheduler返回值是一个函数
const scheduler = max => {
return function (fn) {
}
}
s的返回值可以调用then方法,那么s返回值是一个Promise对象
const scheduler = max => {
return function (fn) {
return new Promise()
}
}
- 如果按照以上分析的顺序执行,那么我们需要一个
queue来存储需要执行的任务,然后还需要一个异步任务来在所有任务存储之后开始执行
const scheduler = max => {
const queue = [] // 用来存储任务的队列
let pending = false // 用来判断当前的异步任务有没有执行
function run () {} // 用来执行存储的任务
return function (fn) {
return new Promise(resolve => {
queue.push(fn)
if (!pending) {
pending = true
Promise.resolve().then(run)
}
})
}
}
- 当我们开始执行任务的时候,我们怎么知道正在执行了多少任务?我们怎么知道执行到了哪个任务?于是新增
runningCount表示正在执行多少任务,index表示执行到了那个任务
const scheduler = max => {
const queue = [] // 用来存储任务的队列
let pending = false // 用来判断当前的异步任务有没有执行
let runningCount = 0
let index = 0
function run () {} // 用来执行存储的任务
return function (fn) {
return new Promise(resolve => {
queue.push(fn)
if (!pending) {
pending = true
Promise.resolve().then(run)
}
})
}
}
- 现在有出现一个问题就是,我们如何知道当前运行的任务执行完了?然后通知下一个任务开始执行?所以我们可以把放进队列的任务进行封装一下,可以传入
f参数
const scheduler = max => {
const queue = [] // 用来存储任务的队列
let pending = false // 用来判断当前的异步任务有没有执行
let runningCount = 0
let index = 0
function run () {} // 用来执行存储的任务
return function (fn) {
return new Promise(resolve => {
queue.push(function (f) {
fn()
f()
})
if (!pending) {
pending = true
Promise.resolve().then(run)
}
})
}
}
- 所以
run开始执行的时候,需要递归执行,即当有任务完成的时候,需要通知下一个任务入队列,所以每个递归函数都会有个终止条件,这个终止条件就是index大于queue中的最后的那个下标
const scheduler = max => {
const queue = [] // 用来存储任务的队列
let pending = false // 用来判断当前的异步任务有没有执行
let runningCount = 0
let index = 0
function run () { // 用来执行存储的任务
if (index >= queue.length) return
while(runningCount < max) {
queue[index](() => {
// 这里面的是任务完成之后的回调
runningCount-- // 当前正在运行的额任务数量减一
run() // 下一个任务开始执行
})
runningCount++ // 正在执行的数量+1
index ++ // 队列中执行的任务的下标 + 1
}
}
return function (fn) {
return new Promise(resolve => {
queue.push(function (f) {
fn()
f()
})
if (!pending) {
pending = true
Promise.resolve().then(run)
}
})
}
}
- 写成上面那样发现执行顺序没有达到要求,额,这是为啥?因为
fn函数都是异步的,其实我们没有等他们执行完毕就通知下一个进来了,所以async await排上用场了
const scheduler = max => {
const queue = [] // 用来存储任务的队列
let pending = false // 用来判断当前的异步任务有没有执行
let runningCount = 0
let index = 0
function run () { // 用来执行存储的任务
if (index >= queue.length) return
while(runningCount < max) {
queue[index](() => {
// 这里面的是任务完成之后的回调
runningCount-- // 当前正在运行的额任务数量减一
run() // 下一个任务开始执行
})
runningCount++ // 正在执行的数量+1
index ++ // 队列中执行的任务的下标 + 1
}
}
return function (fn) {
return new Promise(resolve => {
queue.push(async function (f) {
await fn()
f()
})
if (!pending) {
pending = true
Promise.resolve().then(run)
}
})
}
}
- 顺序是对了,可是后续的
then没有操作啊,这是为啥啊?原因很简单,我们没有任务的返回值给暴露出来,所以
const scheduler = max => {
const queue = [] // 用来存储任务的队列
let pending = false // 用来判断当前的异步任务有没有执行
let runningCount = 0
let index = 0
function run () { // 用来执行存储的任务
if (index >= queue.length) return
while(runningCount < max) {
queue[index](() => {
// 这里面的是任务完成之后的回调
runningCount-- // 当前正在运行的额任务数量减一
run() // 下一个任务开始执行
})
runningCount++ // 正在执行的数量+1
index ++ // 队列中执行的任务的下标 + 1
}
}
return function (fn) {
return new Promise(resolve => {
queue.push(async function (f) {
const res = await fn()
f()
resolve(res) // 将执行结果 resolve 出去
})
if (!pending) {
pending = true
Promise.resolve().then(run)
}
})
}
}
3. 题目总结
这道题其实考察更多的是js中同步异步的执行方式,闭包,队列的基础操作,递归,函数式编程,async await等