You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
4.1 KiB
178 lines
4.1 KiB
1 month ago
|
const { isArray } = require('./utils/isArray');
|
||
|
|
||
|
const proto = exports;
|
||
|
|
||
|
proto._parallelNode = async function _parallelNode(todo, parallel, fn, sourceData) {
|
||
|
const that = this;
|
||
|
// upload in parallel
|
||
|
const jobErr = [];
|
||
|
let jobs = [];
|
||
|
const tempBatch = todo.length / parallel;
|
||
|
const remainder = todo.length % parallel;
|
||
|
const batch = remainder === 0 ? tempBatch : (todo.length - remainder) / parallel + 1;
|
||
|
let taskIndex = 1;
|
||
|
for (let i = 0; i < todo.length; i++) {
|
||
|
if (that.isCancel()) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (sourceData) {
|
||
|
jobs.push(fn(that, todo[i], sourceData));
|
||
|
} else {
|
||
|
jobs.push(fn(that, todo[i]));
|
||
|
}
|
||
|
|
||
|
if (jobs.length === parallel || (taskIndex === batch && i === todo.length - 1)) {
|
||
|
try {
|
||
|
taskIndex += 1;
|
||
|
/* eslint no-await-in-loop: [0] */
|
||
|
await Promise.all(jobs);
|
||
|
} catch (err) {
|
||
|
jobErr.push(err);
|
||
|
}
|
||
|
jobs = [];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return jobErr;
|
||
|
};
|
||
|
|
||
|
proto._parallel = function _parallel(todo, parallel, jobPromise) {
|
||
|
const that = this;
|
||
|
return new Promise(resolve => {
|
||
|
const _jobErr = [];
|
||
|
if (parallel <= 0 || !todo) {
|
||
|
resolve(_jobErr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
function onlyOnce(fn) {
|
||
|
return function (...args) {
|
||
|
if (fn === null) throw new Error('Callback was already called.');
|
||
|
const callFn = fn;
|
||
|
fn = null;
|
||
|
callFn.apply(this, args);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function createArrayIterator(coll) {
|
||
|
let i = -1;
|
||
|
const len = coll.length;
|
||
|
return function next() {
|
||
|
return ++i < len && !that.isCancel() ? { value: coll[i], key: i } : null;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const nextElem = createArrayIterator(todo);
|
||
|
let done = false;
|
||
|
let running = 0;
|
||
|
let looping = false;
|
||
|
|
||
|
function iterateeCallback(err) {
|
||
|
running -= 1;
|
||
|
if (err) {
|
||
|
done = true;
|
||
|
_jobErr.push(err);
|
||
|
resolve(_jobErr);
|
||
|
} else if (done && running <= 0) {
|
||
|
done = true;
|
||
|
resolve(_jobErr);
|
||
|
} else if (!looping) {
|
||
|
/* eslint no-use-before-define: [0] */
|
||
|
if (that.isCancel()) {
|
||
|
resolve(_jobErr);
|
||
|
} else {
|
||
|
replenish();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function iteratee(value, callback) {
|
||
|
jobPromise(value)
|
||
|
.then(result => {
|
||
|
callback(null, result);
|
||
|
})
|
||
|
.catch(err => {
|
||
|
callback(err);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function replenish() {
|
||
|
looping = true;
|
||
|
while (running < parallel && !done && !that.isCancel()) {
|
||
|
const elem = nextElem();
|
||
|
if (elem === null || _jobErr.length > 0) {
|
||
|
done = true;
|
||
|
if (running <= 0) {
|
||
|
resolve(_jobErr);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
running += 1;
|
||
|
iteratee(elem.value, onlyOnce(iterateeCallback));
|
||
|
}
|
||
|
looping = false;
|
||
|
}
|
||
|
|
||
|
replenish();
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* cancel operation, now can use with multipartUpload
|
||
|
* @param {Object} abort
|
||
|
* {String} anort.name object key
|
||
|
* {String} anort.uploadId upload id
|
||
|
* {String} anort.options timeout
|
||
|
*/
|
||
|
proto.cancel = function cancel(abort) {
|
||
|
this.options.cancelFlag = true;
|
||
|
|
||
|
if (isArray(this.multipartUploadStreams)) {
|
||
|
this.multipartUploadStreams.forEach(_ => {
|
||
|
if (_.destroyed === false) {
|
||
|
const err = {
|
||
|
name: 'cancel',
|
||
|
message: 'cancel'
|
||
|
};
|
||
|
_.destroy(err);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
this.multipartUploadStreams = [];
|
||
|
if (abort) {
|
||
|
this.abortMultipartUpload(abort.name, abort.uploadId, abort.options);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
proto.isCancel = function isCancel() {
|
||
|
return this.options.cancelFlag;
|
||
|
};
|
||
|
|
||
|
proto.resetCancelFlag = function resetCancelFlag() {
|
||
|
this.options.cancelFlag = false;
|
||
|
};
|
||
|
|
||
|
proto._stop = function _stop() {
|
||
|
this.options.cancelFlag = true;
|
||
|
};
|
||
|
|
||
|
// cancel is not error , so create an object
|
||
|
proto._makeCancelEvent = function _makeCancelEvent() {
|
||
|
const cancelEvent = {
|
||
|
status: 0,
|
||
|
name: 'cancel'
|
||
|
};
|
||
|
return cancelEvent;
|
||
|
};
|
||
|
|
||
|
// abort is not error , so create an object
|
||
|
proto._makeAbortEvent = function _makeAbortEvent() {
|
||
|
const abortEvent = {
|
||
|
status: 0,
|
||
|
name: 'abort',
|
||
|
message: 'upload task has been abort'
|
||
|
};
|
||
|
return abortEvent;
|
||
|
};
|