mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-18 11:44:05 +08:00
feat(@angular-devkit/core): add a reuse JobStrategy
It allows a job to be reused if it's still running. This includes redirecting the inputs to the new job.
This commit is contained in:
parent
4571197326
commit
988835d024
@ -5,10 +5,16 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import { Observable, concat, of } from 'rxjs';
|
import { Observable, Subject, concat, of } from 'rxjs';
|
||||||
import { ignoreElements, share, shareReplay } from 'rxjs/operators';
|
import { finalize, ignoreElements, share, shareReplay, tap } from 'rxjs/operators';
|
||||||
import { JsonValue } from '../../json';
|
import { JsonValue } from '../../json';
|
||||||
import { JobDescription, JobHandler, JobHandlerContext, JobOutboundMessage } from './api';
|
import {
|
||||||
|
JobDescription,
|
||||||
|
JobHandler,
|
||||||
|
JobHandlerContext, JobInboundMessage,
|
||||||
|
JobOutboundMessage,
|
||||||
|
JobOutboundMessageKind,
|
||||||
|
} from './api';
|
||||||
|
|
||||||
const stableStringify = require('fast-json-stable-stringify');
|
const stableStringify = require('fast-json-stable-stringify');
|
||||||
|
|
||||||
@ -49,6 +55,64 @@ export namespace strategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JobStrategy that will always reuse a running job, and restart it if the job ended.
|
||||||
|
* @param replayMessages Replay ALL messages if a job is reused, otherwise just hook up where it
|
||||||
|
* is.
|
||||||
|
*/
|
||||||
|
export function reuse<
|
||||||
|
A extends JsonValue = JsonValue,
|
||||||
|
I extends JsonValue = JsonValue,
|
||||||
|
O extends JsonValue = JsonValue,
|
||||||
|
>(replayMessages = false): JobStrategy<A, I, O> {
|
||||||
|
let inboundBus = new Subject<JobInboundMessage<I>>();
|
||||||
|
let runContext: JobHandlerContext | null = null;
|
||||||
|
let run: Observable<JobOutboundMessage<O>> | null = null;
|
||||||
|
let state: JobOutboundMessage<O> | null = null;
|
||||||
|
|
||||||
|
return (handler, options) => {
|
||||||
|
const newHandler = (argument: A, context: JobHandlerContext<A, I, O>) => {
|
||||||
|
// Forward inputs.
|
||||||
|
const subscription = context.inboundBus.subscribe(inboundBus);
|
||||||
|
|
||||||
|
if (run) {
|
||||||
|
return concat(
|
||||||
|
// Update state.
|
||||||
|
of(state),
|
||||||
|
run,
|
||||||
|
).pipe(
|
||||||
|
finalize(() => subscription.unsubscribe()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
run = handler(argument, { ...context, inboundBus: inboundBus.asObservable() }).pipe(
|
||||||
|
tap(
|
||||||
|
message => {
|
||||||
|
if (message.kind == JobOutboundMessageKind.Start
|
||||||
|
|| message.kind == JobOutboundMessageKind.OnReady
|
||||||
|
|| message.kind == JobOutboundMessageKind.End) {
|
||||||
|
state = message;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
() => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
inboundBus = new Subject<JobInboundMessage<I>>();
|
||||||
|
run = null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
replayMessages ? shareReplay() : share(),
|
||||||
|
);
|
||||||
|
runContext = context;
|
||||||
|
|
||||||
|
return run;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.assign(newHandler, handler, options || {});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a JobStrategy that will reuse a running job if the argument matches.
|
* Creates a JobStrategy that will reuse a running job if the argument matches.
|
||||||
* @param replayMessages Replay ALL messages if a job is reused, otherwise just hook up where it
|
* @param replayMessages Replay ALL messages if a job is reused, otherwise just hook up where it
|
||||||
|
@ -131,6 +131,73 @@ describe('strategy.serialize()', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('strategy.reuse()', () => {
|
||||||
|
let registry: SimpleJobRegistry;
|
||||||
|
let scheduler: SimpleScheduler;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
registry = new SimpleJobRegistry();
|
||||||
|
scheduler = new SimpleScheduler(registry);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works', async () => {
|
||||||
|
let started = 0;
|
||||||
|
let finished = 0;
|
||||||
|
|
||||||
|
registry.register(strategy.reuse()(createJobHandler((input: number[]) => {
|
||||||
|
started++;
|
||||||
|
|
||||||
|
return new Promise<number>(
|
||||||
|
resolve => setTimeout(() => {
|
||||||
|
finished++;
|
||||||
|
resolve(input.reduce((a, c) => a + c, 0));
|
||||||
|
}, 10),
|
||||||
|
);
|
||||||
|
})), {
|
||||||
|
argument: { items: { type: 'number' } },
|
||||||
|
output: { type: 'number' },
|
||||||
|
name: 'add',
|
||||||
|
});
|
||||||
|
|
||||||
|
const job1 = await scheduler.schedule('add', [1, 2, 3, 4]);
|
||||||
|
const job2 = await scheduler.schedule('add', []);
|
||||||
|
expect(started).toBe(0);
|
||||||
|
expect(finished).toBe(0);
|
||||||
|
|
||||||
|
job1.output.subscribe();
|
||||||
|
expect(started).toBe(1);
|
||||||
|
expect(finished).toBe(0);
|
||||||
|
|
||||||
|
job2.output.subscribe();
|
||||||
|
expect(started).toBe(1); // job2 is reusing job1.
|
||||||
|
expect(finished).toBe(0);
|
||||||
|
|
||||||
|
let result = await job1.output.toPromise();
|
||||||
|
expect(result).toBe(10);
|
||||||
|
expect(started).toBe(1);
|
||||||
|
expect(finished).toBe(1);
|
||||||
|
expect(job1.state).toBe(JobState.Ended);
|
||||||
|
expect(job2.state).toBe(JobState.Ended);
|
||||||
|
|
||||||
|
const job3 = await scheduler.schedule('add', [1, 2, 3, 4, 5]);
|
||||||
|
const job4 = await scheduler.schedule('add', []);
|
||||||
|
job3.output.subscribe();
|
||||||
|
expect(started).toBe(2);
|
||||||
|
expect(finished).toBe(1);
|
||||||
|
|
||||||
|
job4.output.subscribe();
|
||||||
|
expect(started).toBe(2); // job4 is reusing job3.
|
||||||
|
expect(finished).toBe(1);
|
||||||
|
|
||||||
|
result = await job3.output.toPromise();
|
||||||
|
expect(result).toBe(15);
|
||||||
|
expect(started).toBe(2);
|
||||||
|
expect(finished).toBe(2);
|
||||||
|
expect(job3.state).toBe(JobState.Ended);
|
||||||
|
expect(job4.state).toBe(JobState.Ended);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('strategy.memoize()', () => {
|
describe('strategy.memoize()', () => {
|
||||||
let registry: SimpleJobRegistry;
|
let registry: SimpleJobRegistry;
|
||||||
let scheduler: SimpleScheduler;
|
let scheduler: SimpleScheduler;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user