Skip to content

Instantly share code, notes, and snippets.

@Semigradsky
Last active March 29, 2018 09:03
Show Gist options
  • Save Semigradsky/45b4f9628360f812b3640fa979f3a1c4 to your computer and use it in GitHub Desktop.
Save Semigradsky/45b4f9628360f812b3640fa979f3a1c4 to your computer and use it in GitHub Desktop.
(() => {
class Parallel {
constructor({ parallelJobs }) {
this.parallelJobs = parallelJobs;
this.jobsQueue = [];
this.results = [];
}
_flatten(arrray) {
return arrray.reduce((acc, value) => acc.concat(value), []);
}
_runParallel(jobs, done) {
let countCollectedValues = 0;
const values = [];
function collectValues({ index, value }) {
values[index] = value;
countCollectedValues++;
if (countCollectedValues === jobs.length) {
done(values);
}
}
jobs.forEach((job, index) => job((value) => collectValues({ index, value })));
}
_runEach(actions, done) {
let countCollectedValues = 0;
const values = [];
function collectValues({ index, value }) {
values[index] = value;
countCollectedValues++;
if (countCollectedValues === actions.length) {
done(values);
} else {
doAction();
}
}
function doAction() {
const action = actions[countCollectedValues];
action((value) => collectValues({ index: countCollectedValues, value }))
}
doAction();
}
_doWithLimit(actions, limit, done) {
let actionsCopy = [...actions];
const jobsBatches = [];
while (actionsCopy.length) {
const limitedActions = actionsCopy.slice(0, limit);
jobsBatches.push((done) => {
setTimeout(() => {
this._runParallel(limitedActions, done);
}, 0)
});
actionsCopy = actionsCopy.slice(limit);
}
this._runEach(jobsBatches, done);
}
job(callback) {
this.jobsQueue.push(callback);
return this;
}
done(callback) {
this._doWithLimit(this.jobsQueue, this.parallelJobs, (results) => {
callback(this._flatten(results));
});
}
}
const runner = new Parallel({
parallelJobs: 2
});
let result = 'before/';
runner
.job(step0)
.job(step1)
.job(step2)
.job(step3)
.job(step4)
.done(onDone);
result += 'after/';
function step0(done) {
console.log('step0');
result += 'step0/';
done('step0');
}
function step1(done) {
console.log('step1');
result += 'step1/';
setTimeout(done, 3000, 'step1');
}
setTimeout(() => result += 'after0-1/', 2500);
function step2(done) {
console.log('step2');
result += 'step2/';
setTimeout(done, 1500, 'step2');
}
function step3(done) {
console.log('step3');
result += 'step3/';
setTimeout(done, 2000, 'step3');
}
setTimeout(() => result += 'after2-3/', 4500);
function step4(done) {
console.log('step4');
result += 'step4/';
setTimeout(done, 500, 'step4');
}
setTimeout(() => result += 'after4/', 5500);
let isPassed = false;
function onDone(results) {
console.log('onDone', results, result);
console.assert(Array.isArray(results), 'expect result to be array');
console.assert(results.length === 5, 'the results length must be 5');
console.assert(results[0] === 'step0', 'Wrong answer 1');
console.assert(results[1] === 'step1', 'Wrong answer 2');
console.assert(results[2] === 'step2', 'Wrong answer 2');
console.assert(results[3] === 'step3', 'Wrong answer 3');
console.assert(results[4] === 'step4', 'Wrong answer 4');
console.assert(result === 'before/after/step0/step1/after0-1/step2/step3/after2-3/step4/after4/', 'Wrong steps');
console.log('Thanks, all works fine');
isPassed = true;
}
setTimeout(() => {
if(isPassed) return;
console.error('Test is not done.');
}, 6000);
})();
(() => {
class Parallel {
constructor({ parallelJobs }) {
this.parallelJobs = parallelJobs;
this.jobsQueue = [];
this.results = [];
}
async _runAsync(action) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(action());
}, 0)
});
}
async _runParallel(actions) {
return Promise.all(
actions.map((action) => new Promise(resolve => action(resolve)))
);
}
async _doWithLimit(actions, limit) {
let actionsCopy = [...actions];
let results = [];
const actionsBatches = [];
while (actionsCopy.length) {
const limitedActions = actionsCopy.slice(0, limit);
actionsBatches.push(limitedActions);
actionsCopy = actionsCopy.slice(limit);
}
for (const actions of actionsBatches) {
const res = await this._runAsync(() => this._runParallel(actions));
results = results.concat(res);
}
return results;
}
job(callback) {
this.jobsQueue.push(callback);
return this;
}
async done(callback) {
const results = await this._doWithLimit(this.jobsQueue, this.parallelJobs);
callback(results);
}
}
const runner = new Parallel({
parallelJobs: 2
});
let result = 'before/';
runner
.job(step0)
.job(step1)
.job(step2)
.job(step3)
.job(step4)
.done(onDone);
result += 'after/';
function step0(done) {
console.log('step0');
result += 'step0/';
done('step0');
}
function step1(done) {
console.log('step1');
result += 'step1/';
setTimeout(done, 3000, 'step1');
}
setTimeout(() => result += 'after0-1/', 2500);
function step2(done) {
console.log('step2');
result += 'step2/';
setTimeout(done, 1500, 'step2');
}
function step3(done) {
console.log('step3');
result += 'step3/';
setTimeout(done, 2000, 'step3');
}
setTimeout(() => result += 'after2-3/', 4500);
function step4(done) {
console.log('step4');
result += 'step4/';
setTimeout(done, 500, 'step4');
}
setTimeout(() => result += 'after4/', 5500);
let isPassed = false;
function onDone(results) {
console.log('onDone', results, result);
console.assert(Array.isArray(results), 'expect result to be array');
console.assert(results.length === 5, 'the results length must be 5');
console.assert(results[0] === 'step0', 'Wrong answer 1');
console.assert(results[1] === 'step1', 'Wrong answer 2');
console.assert(results[2] === 'step2', 'Wrong answer 2');
console.assert(results[3] === 'step3', 'Wrong answer 3');
console.assert(results[4] === 'step4', 'Wrong answer 4');
console.assert(result === 'before/after/step0/step1/after0-1/step2/step3/after2-3/step4/after4/', 'Wrong steps');
console.log('Thanks, all works fine');
isPassed = true;
}
setTimeout(() => {
if(isPassed) return;
console.error('Test is not done.');
}, 6000);
})();
@Semigradsky
Copy link
Author

test01 - первый вариант на коллбеках
test02 - второй вариант на промисах

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment