multi-tasks 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -7
- package/examples/example0/run.js +18 -9
- package/examples/example1/RunnerConsumer.js +4 -1
- package/examples/example1/RunnerProducer.js +1 -1
- package/examples/example2/RunnerProducer.js +1 -1
- package/index.js +5 -5
- package/package.json +1 -1
- package/workers/SuperConsumer.js +34 -19
- package/workers/SuperProducer.js +16 -4
package/README.md
CHANGED
|
@@ -12,14 +12,38 @@ How to use:
|
|
|
12
12
|
```javascript
|
|
13
13
|
//see examples/example0
|
|
14
14
|
let multiTasks = require('multi-tasks').multiTasks;
|
|
15
|
+
//You need to provide the data of all subtasks, multi-tasks will automatically split them and execute them in a multi-threaded manner
|
|
16
|
+
let alltasks = [];
|
|
17
|
+
for(let i=0;i<12;i++){
|
|
18
|
+
let task_props = {
|
|
19
|
+
index: i,
|
|
20
|
+
name: `task-${i}`,
|
|
21
|
+
description: `This prop is for a subtask`
|
|
22
|
+
};
|
|
23
|
+
alltasks.push(task_props);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//You need to provide a process function to handle a certain sub-task and return the result data
|
|
27
|
+
let processTask = (task)=>{
|
|
28
|
+
console.log('this is the subtask data you created above:', task);
|
|
29
|
+
let {index} = task;
|
|
30
|
+
|
|
31
|
+
//any exceptions will be captured
|
|
32
|
+
if(index===3) throw 'exception';
|
|
33
|
+
if(index===4) return Promise.reject({err:'a test error'});
|
|
34
|
+
|
|
35
|
+
//the returned data will be saved
|
|
36
|
+
return Promise.resolve({
|
|
37
|
+
message: 'this is a result from a demo, random data=' + Math.random()
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
15
41
|
multiTasks({
|
|
16
|
-
tasks:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
quotaOfEachTask:2, //The number of tasks assigned to each worker
|
|
22
|
-
numberOfWorks: 6
|
|
42
|
+
tasks: alltasks,
|
|
43
|
+
processTask,
|
|
44
|
+
multi_task_parent_folder: `C:/tmp-demo`, //a directory to store progress and results files, you can check the progress here
|
|
45
|
+
quotaOfEachTask:1, //The number of subtasks assigned to each worker at one time, the default is 1
|
|
46
|
+
numberOfWorks: 6 //Assign how many workers are working in parallel
|
|
23
47
|
});
|
|
24
48
|
|
|
25
49
|
```
|
|
@@ -47,6 +71,8 @@ node examples/example1/run
|
|
|
47
71
|
|
|
48
72
|
Changelog:
|
|
49
73
|
|
|
74
|
+
- 1.2.2 Update README and examples
|
|
75
|
+
- 1.2.1 Handle exceptions and errors in subtasks
|
|
50
76
|
- 1.2.0 Simplified usage by providing the function way and support return Promise
|
|
51
77
|
- 1.1.4 Remove make-dir
|
|
52
78
|
- 1.1.3 Simplified usage, see example0
|
|
@@ -56,3 +82,7 @@ Changelog:
|
|
|
56
82
|
- 1.0.8 Fix examples
|
|
57
83
|
- 1.0.7 Remove moment
|
|
58
84
|
- 1.0.6 Performance optimization
|
|
85
|
+
|
|
86
|
+
Github:
|
|
87
|
+
|
|
88
|
+
[https://github.com/zhanglei923/multi-tasks](https://github.com/zhanglei923/multi-tasks)
|
package/examples/example0/run.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
let multiTasks = require('../../index').multiTasks;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
//You need to provide the data of all subtasks, multi-tasks will automatically split them and execute them in a multi-threaded manner
|
|
4
|
+
let alltasks = [];
|
|
5
|
+
for(let i=0;i<12;i++){
|
|
5
6
|
let task_props = {
|
|
6
7
|
index: i,
|
|
7
8
|
name: `task-${i}`,
|
|
@@ -10,17 +11,25 @@ for(let i=0;i<200;i++){
|
|
|
10
11
|
alltasks.push(task_props);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
//You need to provide a process function to handle a certain sub-task and return the result data
|
|
15
|
+
let processTask = (task)=>{
|
|
16
|
+
console.log('this is the subtask data you created above:', task);
|
|
17
|
+
let {index} = task;
|
|
18
|
+
|
|
19
|
+
//any exceptions will be captured
|
|
20
|
+
if(index===3) throw 'exception';
|
|
21
|
+
if(index===4) return Promise.reject({err:'a test error'});
|
|
22
|
+
|
|
23
|
+
//the returned data will be saved
|
|
15
24
|
return Promise.resolve({
|
|
16
25
|
message: 'this is a result from a demo, random data=' + Math.random()
|
|
17
26
|
});
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
multiTasks({
|
|
21
|
-
tasks: alltasks,
|
|
22
|
-
|
|
23
|
-
multi_task_parent_folder: `C:/tmp-demo`, //a directory to store progress and results files
|
|
24
|
-
quotaOfEachTask:
|
|
25
|
-
numberOfWorks: 6
|
|
30
|
+
tasks: alltasks,
|
|
31
|
+
processTask,
|
|
32
|
+
multi_task_parent_folder: `C:/tmp-demo`, //a directory to store progress and results files, you can check the progress here
|
|
33
|
+
quotaOfEachTask:1, //The number of subtasks assigned to each worker at one time, the default is 1
|
|
34
|
+
numberOfWorks: 6 //Assign how many workers are working in parallel
|
|
26
35
|
});
|
|
@@ -2,9 +2,12 @@ let SuperConsumer = require('../../index').SuperConsumer;
|
|
|
2
2
|
|
|
3
3
|
class TestConsumer extends SuperConsumer {
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
processTask(actionData){
|
|
6
6
|
console.log(`processing data:`, actionData);
|
|
7
7
|
//you can also return a data instead of a promise object
|
|
8
|
+
let {index} = actionData;
|
|
9
|
+
if(index===3) throw 'exception';
|
|
10
|
+
if(index===4) return Promise.reject({err:'a test error'});
|
|
8
11
|
return new Promise((resolve) => {
|
|
9
12
|
setTimeout(() => {
|
|
10
13
|
let result = {
|
|
@@ -13,7 +13,7 @@ class TestProducer extends SuperProducer {
|
|
|
13
13
|
/**
|
|
14
14
|
* Generate all your tasks in this method, return as an array;
|
|
15
15
|
* These data will be allocated to multiple threads (workers) for running;
|
|
16
|
-
* You can obtain each of the data and process it in the '
|
|
16
|
+
* You can obtain each of the data and process it in the 'processTask' method of your consumer.
|
|
17
17
|
*
|
|
18
18
|
* **/
|
|
19
19
|
|
|
@@ -13,7 +13,7 @@ class TestProducer extends SuperProducer {
|
|
|
13
13
|
/**
|
|
14
14
|
* Generate all your tasks in this method, return as an array;
|
|
15
15
|
* These data will be allocated to multiple threads (workers) for running;
|
|
16
|
-
* You can obtain each of the data and process it in the '
|
|
16
|
+
* You can obtain each of the data and process it in the 'processTask' method of your consumer.
|
|
17
17
|
*
|
|
18
18
|
* **/
|
|
19
19
|
|
package/index.js
CHANGED
|
@@ -5,8 +5,8 @@ const multiTasks = (config)=>{
|
|
|
5
5
|
if(!config.tasks){
|
|
6
6
|
throw 'Please provide tasks data';
|
|
7
7
|
};
|
|
8
|
-
if(!config.
|
|
9
|
-
throw 'Please provide
|
|
8
|
+
if(!config.processTask){
|
|
9
|
+
throw 'Please provide processTask function';
|
|
10
10
|
};
|
|
11
11
|
class TestProducer extends SuperProducer {
|
|
12
12
|
// constructor
|
|
@@ -18,9 +18,9 @@ const multiTasks = (config)=>{
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
class TestConsumer extends SuperConsumer {
|
|
21
|
-
|
|
22
|
-
console.log(`processing data:`,
|
|
23
|
-
let result = config.
|
|
21
|
+
processTask(taskProp){
|
|
22
|
+
console.log(`processing data:`, taskProp);
|
|
23
|
+
let result = config.processTask(taskProp);
|
|
24
24
|
return Promise.resolve(result);
|
|
25
25
|
}
|
|
26
26
|
}
|
package/package.json
CHANGED
package/workers/SuperConsumer.js
CHANGED
|
@@ -12,38 +12,48 @@ class SuperConsumer {
|
|
|
12
12
|
setTaskTrunkInfo(trunkInfo){
|
|
13
13
|
this.trunkInfo = trunkInfo;
|
|
14
14
|
}
|
|
15
|
-
_run (mainTaskInfo, subTaskInfo, actions, results, callback){
|
|
15
|
+
_run (mainTaskInfo, subTaskInfo, actions, results, errors, callback){
|
|
16
16
|
if(actions.length === 0) return callback({
|
|
17
17
|
finishedAt: new Date()*1,
|
|
18
18
|
results,
|
|
19
|
+
errors,
|
|
19
20
|
subTaskInfo
|
|
20
21
|
});
|
|
21
|
-
let
|
|
22
|
-
mainid: this.mainid,
|
|
23
|
-
subid: this.subid
|
|
24
|
-
};
|
|
25
|
-
let actionData = actions.shift();
|
|
22
|
+
let taskProp = actions.shift();
|
|
26
23
|
|
|
27
|
-
if(this.
|
|
28
|
-
let result
|
|
24
|
+
if(this.processTask){
|
|
25
|
+
let result;
|
|
26
|
+
let hasException = false;
|
|
27
|
+
try{
|
|
28
|
+
result = this.processTask(taskProp);//rewrite this method in subclass
|
|
29
|
+
}catch(e){
|
|
30
|
+
hasException = true;
|
|
31
|
+
result = {taskProp, exception: 'Exception:' + e.toString()}
|
|
32
|
+
}
|
|
29
33
|
if(typeof result === 'undefined') result = {};
|
|
30
34
|
let ispromise = result instanceof Promise;
|
|
31
|
-
if(!
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if(!ispromise){result = Promise.resolve(result);}
|
|
36
|
+
result.then((taskResult)=>{
|
|
37
|
+
if(!hasException){
|
|
38
|
+
results.push({taskProp, taskResult});
|
|
39
|
+
}else{
|
|
40
|
+
errors.push(taskResult);
|
|
41
|
+
}
|
|
42
|
+
}).catch((e)=>{
|
|
43
|
+
errors.push({taskProp, exception: 'Exception:' + e.toString()});
|
|
44
|
+
}).finally(()=>{
|
|
45
|
+
this._run(mainTaskInfo, subTaskInfo, actions, results, errors, callback);
|
|
37
46
|
});
|
|
38
47
|
}else{
|
|
39
|
-
this._run(mainTaskInfo, subTaskInfo, actions, results, callback);
|
|
48
|
+
this._run(mainTaskInfo, subTaskInfo, actions, results, errors, callback);
|
|
40
49
|
}
|
|
41
50
|
};
|
|
42
51
|
run (mainTaskInfo, subTaskInfo, taskInfo, callback){
|
|
43
52
|
let results = [];
|
|
53
|
+
let errors = [];
|
|
44
54
|
let actions = subTaskInfo.actions;
|
|
45
55
|
mainTaskInfo.defination = this.trunkInfo.defination;
|
|
46
|
-
this._run(mainTaskInfo, subTaskInfo, actions, results, (result)=>{
|
|
56
|
+
this._run(mainTaskInfo, subTaskInfo, actions, results, errors, (result)=>{
|
|
47
57
|
callback(result)
|
|
48
58
|
})
|
|
49
59
|
|
|
@@ -61,13 +71,18 @@ class SuperConsumer {
|
|
|
61
71
|
let subid = subTaskInfo.subid;
|
|
62
72
|
let taskfolderNew = mainTaskInfo.taskFolderNew;
|
|
63
73
|
let taskFolderResult = mainTaskInfo.taskFolderResult;
|
|
74
|
+
let taskFolderResultFailed = mainTaskInfo.taskFolderResultFailed;
|
|
64
75
|
this.mvTask(mainid, subid,'new','running', (taskFile)=>{//mark as running
|
|
65
76
|
let taskContent = fs.readFileSync(taskFile, 'utf8');
|
|
66
77
|
let taskInfo = JSON.parse(taskContent);
|
|
67
78
|
this.run(mainTaskInfo, subTaskInfo, taskInfo, (taskReport)=>{//invoke
|
|
68
|
-
let
|
|
79
|
+
let {results, errors} = taskReport;
|
|
80
|
+
let hasError = false;
|
|
81
|
+
if(errors && errors.length > 0) hasError = true;
|
|
82
|
+
let folder = !hasError ? taskFolderResult : taskFolderResultFailed;
|
|
83
|
+
let resultfile = pathutil.resolve(folder, `${subid}.json`)
|
|
69
84
|
fs.writeFileSync(resultfile, JSON.stringify(taskReport));
|
|
70
|
-
this.mvTask(mainid, subid,'running','finished', ()=>{//mark as finished
|
|
85
|
+
this.mvTask(mainid, subid,'running',(hasError ? 'finished_with_errors':'finished'), ()=>{//mark as finished
|
|
71
86
|
resolve({
|
|
72
87
|
mainid,
|
|
73
88
|
subid,
|
|
@@ -79,7 +94,7 @@ class SuperConsumer {
|
|
|
79
94
|
});
|
|
80
95
|
}
|
|
81
96
|
mvTask (mainid, subid, from, to, callback) {
|
|
82
|
-
let __thisdir = this.mainTaskInfo.
|
|
97
|
+
let __thisdir = this.mainTaskInfo.progressRootFolder;
|
|
83
98
|
let oldPath = pathutil.resolve(__thisdir, `./${from}/${subid}.task.json`);
|
|
84
99
|
let newPath = pathutil.resolve(__thisdir, `./${to}/${subid}.task.json`);
|
|
85
100
|
fs.rename(oldPath, newPath, (err) => {
|
package/workers/SuperProducer.js
CHANGED
|
@@ -39,17 +39,25 @@ class SuperProducer {
|
|
|
39
39
|
let taskFolder = this.taskfolder;
|
|
40
40
|
//let taskfolder = pathutil.parse(__filename).dir;
|
|
41
41
|
//let taskFolder = pathutil.resolve(taskfolder, `./tasks/${masterTaskId}`);
|
|
42
|
-
let
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
let
|
|
42
|
+
let progressRootFolder = pathutil.resolve(taskFolder, './progress');
|
|
43
|
+
let resultsRootFolder = pathutil.resolve(taskFolder, './results');
|
|
44
|
+
|
|
45
|
+
let taskFolderNew = pathutil.resolve(progressRootFolder, './new');
|
|
46
|
+
let taskFolderRunning = pathutil.resolve(progressRootFolder, './running');
|
|
47
|
+
let taskFolderFinished = pathutil.resolve(progressRootFolder, './finished');
|
|
48
|
+
let taskFolderFailed= pathutil.resolve(progressRootFolder, './finished_with_errors');
|
|
49
|
+
|
|
50
|
+
let taskFolderResult = pathutil.resolve(resultsRootFolder, './succ');
|
|
51
|
+
let taskFolderResultFailed = pathutil.resolve(resultsRootFolder, './errors');
|
|
46
52
|
//let mainRptFolder = pathutil.resolve(taskfolder, './report');
|
|
47
53
|
|
|
48
54
|
//makeDir.sync(mainRptFolder);
|
|
49
55
|
makeDir.sync(taskFolderNew);
|
|
50
56
|
makeDir.sync(taskFolderRunning);
|
|
51
57
|
makeDir.sync(taskFolderFinished);
|
|
58
|
+
makeDir.sync(taskFolderFailed);
|
|
52
59
|
makeDir.sync(taskFolderResult);
|
|
60
|
+
makeDir.sync(taskFolderResultFailed);
|
|
53
61
|
|
|
54
62
|
this.getAllActions().then((allactions)=>{
|
|
55
63
|
|
|
@@ -72,11 +80,15 @@ class SuperProducer {
|
|
|
72
80
|
masterTaskId,
|
|
73
81
|
allSubTasksId,
|
|
74
82
|
//mainRptFolder,
|
|
83
|
+
progressRootFolder,
|
|
84
|
+
resultsRootFolder,
|
|
85
|
+
|
|
75
86
|
taskFolder,
|
|
76
87
|
taskFolderNew,
|
|
77
88
|
taskFolderRunning,
|
|
78
89
|
taskFolderFinished,
|
|
79
90
|
taskFolderResult,
|
|
91
|
+
taskFolderResultFailed,
|
|
80
92
|
});
|
|
81
93
|
});
|
|
82
94
|
}
|