Skip to content

Commit

Permalink
Support onInit callback to provider (#4866)
Browse files Browse the repository at this point in the history
* sync onInit callback

* add tests for onInit

* revert dev changes

* fix

* update lock

* Create early-nails-hope.md

* use type alias
  • Loading branch information
Feiyang1 authored May 4, 2021
1 parent d095ad3 commit c34ac7a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/early-nails-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@firebase/component": minor
---

Support onInit callback in provider
57 changes: 57 additions & 0 deletions packages/component/src/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,63 @@ describe('Provider', () => {
expect((provider as any).instances.size).to.equal(1);
return expect(servicePromise).to.eventually.deep.equal({ test: true });
});

it('invokes onInit callbacks synchronously', () => {
provider.setComponent(
getFakeComponent(
'test',
() => ({ test: true }),
false,
InstantiationMode.EXPLICIT
)
);
const callback1 = fake();
provider.onInit(callback1);

provider.initialize();
expect(callback1).to.have.been.calledOnce;
});
});

describe('onInit', () => {
it('registers onInit callbacks', () => {
provider.setComponent(
getFakeComponent(
'test',
() => ({ test: true }),
false,
InstantiationMode.EXPLICIT
)
);
const callback1 = fake();
const callback2 = fake();
provider.onInit(callback1);
provider.onInit(callback2);

provider.initialize();
expect(callback1).to.have.been.calledOnce;
expect(callback2).to.have.been.calledOnce;
});

it('returns a function to unregister the callback', () => {
provider.setComponent(
getFakeComponent(
'test',
() => ({ test: true }),
false,
InstantiationMode.EXPLICIT
)
);
const callback1 = fake();
const callback2 = fake();
provider.onInit(callback1);
const unregsiter = provider.onInit(callback2);
unregsiter();

provider.initialize();
expect(callback1).to.have.been.calledOnce;
expect(callback2).to.not.have.been.called;
});
});

describe('Provider (multipleInstances = false)', () => {
Expand Down
39 changes: 38 additions & 1 deletion packages/component/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
InitializeOptions,
InstantiationMode,
Name,
NameServiceMapping
NameServiceMapping,
OnInitCallBack
} from './types';
import { Component } from './component';

Expand All @@ -37,6 +38,7 @@ export class Provider<T extends Name> {
string,
Deferred<NameServiceMapping[T]>
> = new Map();
private onInitCallbacks: Set<OnInitCallBack<T>> = new Set();

constructor(
private readonly name: T,
Expand Down Expand Up @@ -250,9 +252,44 @@ export class Provider<T extends Name> {
instanceDeferred.resolve(instance);
}
}

this.invokeOnInitCallbacks(instance, normalizedIdentifier);

return instance;
}

/**
*
* @param callback - a function that will be invoked after the provider has been initialized by calling provider.initialize().
* The function is invoked SYNCHRONOUSLY, so it should not execute any longrunning tasks in order to not block the program.
*
* @returns a function to unregister the callback
*/
onInit(callback: OnInitCallBack<T>): () => void {
this.onInitCallbacks.add(callback);

return () => {
this.onInitCallbacks.delete(callback);
};
}

/**
* Invoke onInit callbacks synchronously
* @param instance the service instance`
*/
private invokeOnInitCallbacks(
instance: NameServiceMapping[T],
identifier: string
): void {
for (const callback of this.onInitCallbacks) {
try {
callback(instance, identifier);
} catch {
// ignore errors in the onInit callback
}
}
}

private getOrInitializeService({
instanceIdentifier,
options = {}
Expand Down
5 changes: 5 additions & 0 deletions packages/component/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,8 @@ export interface NameServiceMapping {}

export type Name = keyof NameServiceMapping;
export type Service = NameServiceMapping[Name];

export type OnInitCallBack<T extends Name> = (
instance: NameServiceMapping[T],
identifier: string
) => void;

0 comments on commit c34ac7a

Please sign in to comment.