题目描述
依次顺序执行一系列任务
所有任务全部完成后可以得到每个任务的执行结果
需要返回两个方法,start 用于启动任务,pause 用于暂停任务
每个任务具有原子性,即不可中断,只能在两个任务之间中断
每个任务无参,异步
function processTasks(...task) {}
实现
首先确定返回值为两个方法,start 和 pause
function processTasks(...task) {
return {
start() {}
pause() {}
}
}
先实现 start 的基本功能:遍历 tasks 并取出任务,依次执行,将收集到的结果保存,当全部任务结束后返回最终结果
function processTasks(...tasks) {
// 保存结果
const result = []
return {
// 任务是异步的,所以 start 也是异步的
async start() {
// 取出任务并执行
for (let i = 0; i < tasks.length; i++) {
console.log(`任务${i} 开始...`)
const r = await tasks[i]();
console.log(`任务${i} 完成!`)
result.push(r)
}
// 任务全部完成,返回最终结果
if (result.length === tasks.length) {
return result
}
},
pause() { }
}
}
然后考虑中断的情况,需要缓存当前执行的任务索引,以及维护暂停的状态
pause 方法要做的就只是将暂停状态更改为 true
在 start 中,将暂停状态更改为 false,循环执行每个任务之后,判断当前暂停状态,如果暂停了,就使用 return 终止 start 函数的执行
function processTasks(...tasks) {
const result = []
// 缓存当前执行的位置
let currentIndex = 0
// 当前执行状态
let isPaused = false
return {
async start() {
// 开始执行
console.log('start: 开始执行...')
isPaused = false
for (; currentIndex < tasks.length; currentIndex++) {
console.log(`任务${currentIndex} 开始...`)
const r = await tasks[currentIndex]();
console.log(`任务${currentIndex} 完成!`)
result.push(r)
// 每次任务执行结束后,检查当前状态
if (isPaused) {
console.log('start: 暂停,等待恢复...')
// 如果暂停了,就终止函数执行
return
}
}
if (result.length === tasks.length) {
return result
}
},
pause() {
console.log('pause: 暂停执行!')
isPaused = true
}
}
}
目前存在一个问题,就是暂停后恢复执行时,会重复执行上次的任务
currentIndex
应该在任务完成后就自增,循环可以改为 while
// ...
while(currentIndex < tasks.length) {
console.log(`任务${currentIndex} 开始...`)
const r = await tasks[currentIndex]();
console.log(`任务${currentIndex} 完成!`)
result.push(r)
// 在每次任务执行后就更新任务索引
currentIndex++
if (isPaused) {
console.log('start: 暂停,等待恢复...')
return
}
}
// ...
目前任务执行与中断的需求就完成了
不过唯一美中不足的就是,每次暂停执行后,start 的函数就会 return,导致 start 返回的 Promise 会在每次暂停执行后默认 resolve 一个 undefined,与要求中的「所有任务全部完成后可以得到每个任务的执行结果」不符
- 如何使 start 方法在所有任务都完成后才会返回最终结果呢?
即 start 方法返回的 Promise 只有在全部任务完成后才 resolve
为了手动显式控制 Promise 的 resolve,所以让 start 方法手动返回一个 Promise;当所有任务完成后,使用 resolve(result)
替换 return result
// start 也不用 async 了
start() {
return new Promise(async (resolve) => {
console.log('start: 开始执行...')
isPaused = false
while(currentIndex < tasks.length) {
console.log(`任务${currentIndex} 开始...`)
const r = await tasks[currentIndex]();
console.log(`任务${currentIndex} 完成!`)
result.push(r)
currentIndex++
if (isPaused) {
console.log('start: 暂停,等待恢复...')
// 这里 return 就不会导致整个 start 的 Promise 返回 undefined 了
// 而是仅仅使得当前的 Promise 进入 pending 状态
return
}
}
if (result.length === tasks.length) {
resolve(result)
}
})
}
这样一来,即使任务中断了,return 不会导致当前 start 返回的 Promise 进入 resolve 状态,而是一直维持 pending 状态
最终代码
function processTasks(...tasks) {
const result = []
let currentIndex = 0
let isPaused = false
return {
start() {
return new Promise(async (resolve) => {
isPaused = false
while(currentIndex < tasks.length) {
const r = await tasks[currentIndex]()
result.push(r)
currentIndex++
if (isPaused) {
return
}
}
if (result.length === tasks.length) {
resolve(result)
}
})
},
pause() {
isPaused = true
}
}
}
测试代码
<body>
<button id="begin">启动任务</button>
<button id="pause">暂停任务</button>
</body>
<script src="./3.js"></script>
<script>
const tasks = []
for (let i = 0; i < 5; i++) {
tasks.push(() => {
return new Promise((resolve) => {
setTimeout(() => resolve(i), 1000)
})
})
}
const processor = processTasks(...tasks)
begin.onclick = async () => {
console.log('点击开始')
const results = await processor.start()
console.log('任务执行完成', results)
}
pause.onclick = () => {
console.log('点击暂停')
processor.pause()
}
</script>