Skip to content

remove rxwait async policy in favour of allowing user defined async policy function #366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/resolve/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ export interface ResolvePolicy {
}

export type PolicyWhen = 'LAZY' | 'EAGER';
export type PolicyAsync = 'WAIT' | 'NOWAIT' | 'RXWAIT';
export type PolicyAsync = 'WAIT' | 'NOWAIT' | CustomAsyncPolicy;
export type CustomAsyncPolicy = <TResolveFnResult, TResolveValue>(data: TResolveFnResult) => Promise<TResolveValue>;

/** @internalapi */
export let resolvePolicies = {
Expand Down
26 changes: 6 additions & 20 deletions src/resolve/resolvable.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/** @publicapi @module resolve */ /** */
import { extend, equals, inArray, identity } from '../common/common';
import { extend, identity } from '../common/common';
import { services } from '../common/coreservices';
import { trace } from '../common/trace';
import { ResolvePolicy, ResolvableLiteral, resolvePolicies } from './interface';
import { ResolvePolicy, ResolvableLiteral, PolicyAsync } from './interface';

import { ResolveContext } from './resolveContext';
import { stringify } from '../common/strings';
Expand Down Expand Up @@ -117,26 +117,12 @@ export class Resolvable implements ResolvableLiteral {
// Invokes the resolve function passing the resolved dependencies as arguments
const invokeResolveFn = (resolvedDeps: any[]) => this.resolveFn.apply(null, resolvedDeps);

/**
* For RXWAIT policy:
*
* Given an observable returned from a resolve function:
* - enables .cache() mode (this allows multicast subscribers)
* - then calls toPromise() (this triggers subscribe() and thus fetches)
* - Waits for the promise, then return the cached observable (not the first emitted value).
*/
const waitForRx = (observable$: any) => {
const cached = observable$.cache(1);
return cached
.take(1)
.toPromise()
.then(() => cached);
};

// If the resolve policy is RXWAIT, wait for the observable to emit something. otherwise pass through.
const node: PathNode = resolveContext.findNode(this);
const state: StateObject = node && node.state;
const maybeWaitForRx = this.getPolicy(state).async === 'RXWAIT' ? waitForRx : identity;

const asyncPolicy: PolicyAsync = this.getPolicy(state).async;
const customAsyncPolicy = isFunction(asyncPolicy) ? asyncPolicy : identity;

// After the final value has been resolved, update the state of the Resolvable
const applyResolvedValue = (resolvedValue: any) => {
Expand All @@ -152,7 +138,7 @@ export class Resolvable implements ResolvableLiteral {
.when()
.then(getResolvableDependencies)
.then(invokeResolveFn)
.then(maybeWaitForRx)
.then(customAsyncPolicy)
.then(applyResolvedValue));
}

Expand Down
66 changes: 52 additions & 14 deletions test/resolveSpec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ResolveContext, StateObject, PathNode, Resolvable, copy } from '../src/index';
import { tail } from '../src/common/common';
import { services } from '../src/common/coreservices';
import { tree2Array } from './_testUtils';
import { copy, CustomAsyncPolicy, PathNode, Resolvable, ResolveContext, StateObject } from '../src/index';
import { UIRouter } from '../src/router';

import { TestingPlugin } from './_testingPlugin';
import { StateRegistry } from '../src/state/stateRegistry';
import { StateService } from '../src/state/stateService';
import { TransitionService } from '../src/transition/transitionService';
import { StateRegistry } from '../src/state/stateRegistry';
import { tail } from '../src/common/common';

import { TestingPlugin } from './_testingPlugin';
import { tree2Array } from './_testUtils';

///////////////////////////////////////////////

Expand Down Expand Up @@ -426,6 +426,7 @@ describe('Resolvables system:', function() {
const ctx = new ResolveContext(path);

let result;

function checkCounts() {
expect(result).toBe('JJ2K');
expect(counts['_J']).toBe(1);
Expand Down Expand Up @@ -561,10 +562,11 @@ describe('Resolvables system:', function() {

describe('NOWAIT Resolve Policy', () => {
it('should allow a transition to complete before the resolve is settled', async done => {
let resolve,
resolvePromise = new Promise(_resolve => {
resolve = _resolve;
});
let resolve;

const resolvePromise = new Promise(_resolve => {
resolve = _resolve;
});

$registry.register({
name: 'nowait',
Expand Down Expand Up @@ -599,10 +601,11 @@ describe('Resolvables system:', function() {
});

it('should wait for WAIT resolves and not wait for NOWAIT resolves', async done => {
let promiseResolveFn,
resolvePromise = new Promise(resolve => {
promiseResolveFn = resolve;
});
let promiseResolveFn;

const resolvePromise = new Promise(resolve => {
promiseResolveFn = resolve;
});

$registry.register({
name: 'nowait',
Expand Down Expand Up @@ -635,4 +638,39 @@ describe('Resolvables system:', function() {
$state.go('nowait');
});
});

describe('custom Resolve Policy', () => {
let customResolvePolicy: CustomAsyncPolicy;

it('should wait for the promise to resolve before finishing the transition', async done => {
let resolve: (value: string) => void;

const resolvePromise: Promise<string> = new Promise(_resolve => {
resolve = _resolve;
});

customResolvePolicy = jasmine.createSpy('customResolvePolicy');

(customResolvePolicy as jasmine.Spy).and.callFake((data: { useMe: Promise<string> }) => {
return data.useMe;
});

resolve('myAwaitedValue');

$registry.register({
name: 'customPolicy',
resolve: {
customWait: () => ({ useMe: resolvePromise }),
},
resolvePolicy: { async: customResolvePolicy },
});

$transitions.onSuccess({}, trans => {
expect(customResolvePolicy).toHaveBeenCalledWith({ useMe: resolvePromise });
expect(trans.injector().get('customWait')).toBe('myAwaitedValue');
});

$state.go('customPolicy').then(done);
});
});
});