标准答案
- 每个任务需要 id、callback、priority、status 和 cancel 方法或取消标记。
- 入队时按优先级排序,优先级高的任务先执行。
- 执行前检查任务是否被取消,被取消就跳过。
- 如果任务是异步的,要处理并发上限、失败状态和 finally 后继续调度。
- 复杂场景还要支持时间片、超时、暂停恢复、重试和任务依赖。
题目解析
任务调度器的核心不是简单 setTimeout,而是“哪些任务先跑、哪些任务可以不跑、跑到一半如何继续推进”。优先级和取消是两个基本控制点。
取消通常不是强行杀掉已经执行的同步函数,而是在任务执行前或异步任务的检查点跳过后续逻辑。对于 fetch 这类 API,可以配合 AbortController 做真正取消。
如果要贴近前端框架,还可以说明时间片和让出主线程:长任务不能一直占用主线程,否则会影响输入响应和渲染。
代码示例
简化版调度器可以按优先级取任务,并在执行前检查取消状态。
JavaScript
function createScheduler() {
const queue = []
function add(task, priority = 0) {
const item = { task, priority, canceled: false }
queue.push(item)
queue.sort((a, b) => b.priority - a.priority)
return () => {
item.canceled = true
}
}
async function run() {
while (queue.length) {
const item = queue.shift()
if (!item.canceled) {
await item.task()
}
}
}
return { add, run }
}常见误区
- 只实现队列先进先出,没有体现优先级。
- 取消时直接从数组删除但没有处理已经开始执行的异步任务。
- 长任务连续执行,不考虑让出主线程和用户交互响应。