任务队列的中断和恢复

题目描述

1
2
3
4
5
6
7
8
9
/**
* 实现一个函数满足以下需求:
* 依次顺序执行一系列任务
* 所有任务全部完成后可以得到每个任务的执行结果
* 需要返回两个方法,start用于启动任务,pause用于暂停任务
* 每个任务具有原子性,即不可中断,只能在两个任务之间中断
* @param {...Function} tasks 任务列表,每个任务无参,异步
*/
function processTasks(...tasks) {}

实现思路

  1. 函数基本结构
    根据题目要求,我们需要返回包含 start 和 pause 两个方法的对象。start 方法用于启动任务队列的执行,pause 方法用于暂停执行。
1
2
3
4
5
6
7
8
function processTasks(...tasks) {
return {
// 启动任务
start() {},
// 暂停任务
pause() {},
};
}
  1. 任务状态管理
    引入 isRunning 状态变量来跟踪当前是否正在执行任务。这确保了任务的原子性,即任务只能在两个任务之间被中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function processTasks(...tasks) {
let isRunning = false; // 表示当前是否正在执行任务
return {
start() {
if (isRunning) {
return;
}
isRunning = true;
// 执行任务
// ...
isRunning = false; // 执行任务完成后,修改状态
return "结果"; // 返回结果
},
pause() {
isRunning = false;
},
};
}
  1. 结果收集
    添加 results 数组来存储每个任务的执行结果。这个数组需要在多次调用 start 方法时保持状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function processTasks(...tasks) {
let isRunning = false;
let results = []; // 存放所有任务的结果(需要定义到 start 函数的外面,因为 start 函数可能重复调用)
return {
start() {
if (isRunning) {
return;
}
isRunning = true;
// 执行任务
// ...
isRunning = false;
return results; // 返回结果
},
// 暂停任务
pause() {
isRunning = false;
},
};
}
  1. 任务执行逻辑
    实现具体的任务执行流程,使用 async/await 确保异步任务的顺序执行,并将每个任务的结果存入 results 数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function processTasks(...tasks) {
let isRunning = false;
let results = [];
return {
async start() {
if (isRunning) {
return;
}
isRunning = true;
// 执行任务
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i]; // 每一个任务都是一个函数
const result = await task(); // 执行当前(异步)任务
results.push(result); // 存放任务结果
}
isRunning = false;
return results;
},
pause() {
isRunning = false;
},
};
}
  1. 中断检查机制
    在每个任务执行后检查 isRunning 状态,如果被暂停则立即终止执行流程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function processTasks(...tasks) {
let isRunning = false;
let results = [];
return {
async start() {
if (isRunning) {
return;
}
isRunning = true;
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
const result = await task();
results.push(result);

// 如果当前已经暂停则需要退出任务的执行
if (!isRunning) {
return;
}
}
isRunning = false;
return results;
},
pause() {
isRunning = false;
},
};
}
  1. 任务进度记录
    使用索引变量 i 来记录当前执行到的任务位置,确保暂停后可以从中断处继续执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function processTasks(...tasks) {
let isRunning = false;
let results = [];
let i = 0; // 记录当前任务的索引
return {
async start() {
if (isRunning) {
return;
}
isRunning = true;
while (i < tasks.length) {
const task = tasks[i++]; // 索引自增
const result = await task();
results.push(result);

if (!isRunning) {
return;
}
}
isRunning = false;
return results;
},
pause() {
isRunning = false;
},
};
}
  1. Promise 封装
    题目要求所有任务全部完成后可以得到每个任务的执行结果,也就是在所有任务还没全部完成前,应该返回一个 pending 状态的 Promise。因此我们将整个执行流程封装在 Promise 中,确保在所有任务完成前返回 pending 状态的 Promise,完成后 resolve 最终结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function processTasks(...tasks) {
let isRunning = false;
let results = [];
let i = 0;
return {
start() {
return new Promise(async (resolve) => {
if (isRunning) {
return; // 返回 pending 状态的 Promise
}
isRunning = true;
while (i < tasks.length) {
const task = tasks[i++];
const result = await task();
results.push(result);

if (!isRunning) {
return; // 返回 pending 状态的 Promise
}
}
isRunning = false;
resolve(results); // 返回结果
});
},
pause() {
isRunning = false;
},
};
}

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 依次顺序执行一系列任务
* 所有任务全部完成后可以得到每个任务的执行结果
* 需要返回两个方法,start用于启动任务,pause用于暂停任务
* 每个任务具有原子性,即不可中断,只能在两个任务之间中断
* @param {...Function} tasks 任务列表,每个任务无参,异步
*/

function processTasks(...tasks) {
let isRunning = false; // 表示当前是否正在执行任务
let results = []; // 存放所有任务的结果(需要定义到start函数的外面,因为start函数可能重复调用)
let i = 0;
return {
// 启动任务
start() {
return new Promise(async (resolve) => {
if (isRunning) {
return; // 返回 pending 状态的 Promise
}
isRunning = true;
// 执行任务
while (i < tasks.length) {
const task = tasks[i++]; // 每一个任务都是一个函数
const result = await task(); // 执行当前(异步)任务
results.push(result); // 存放任务结果

// 如果当前已经暂停则需要退出任务的执行
if (!isRunning) {
return; // 返回 pending 状态的 Promise
}
}
isRunning = false; // 执行任务完成后,修改状态
resolve(results); // 返回结果
});
},
// 暂停任务
pause() {
isRunning = false;
},
};
}

案例测试

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="start">开始任务</button>
<button id="pause">暂停任务</button>
<!-- 通过文件引入封装好的任务队列函数 -->
<script src="./processTasks.js"></script>
<script>
const tasks = []; // 任务数组
// 添加任务
for (let i = 0; i < 5; i++) {
tasks.push(() => {
return new Promise((resolve) => {
console.log(`任务${i + 1}开始了`);
setTimeout(() => {
resolve(i);
console.log(`任务${i + 1}完成了`);
}, 2000);
});
});
}
// 任务封装
const process = processTasks(...tasks);
// 开始任务
start.onclick = async function () {
console.log("点击开始按钮");
const result = await process.start();
console.log("任务执行完成", result);
};
// 暂停任务
pause.onclick = async function () {
console.log("点击暂停按钮");
process.pause();
};
</script>
</body>
</html>