Skip to content

Commit

Permalink
fix(fast-element): do not notify splices for changes pre-subscription…
Browse files Browse the repository at this point in the history
… (v2) (#6006)

* fix(fast-element): do not notify splices for changes pre-subscription

* Change files

Co-authored-by: EisenbergEffect <[email protected]>
  • Loading branch information
EisenbergEffect and EisenbergEffect authored May 23, 2022
1 parent 2b2eeec commit b0d1d70
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix(fast-element): do not notify splices for changes pre-subscription",
"packageName": "@microsoft/fast-element",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,24 @@ describe("The ArrayObserver", () => {
expect(changeArgs![0].removed).members([]);
expect(changeArgs![0].index).equal(0);
});

it("should not deliver splices for changes prior to subscription", async () => {
ArrayObserver.enable();
const array = [1,2,3,4,5];
const observer = Observable.getNotifier(array);
let wasCalled = false;

array.push(6);
observer.subscribe({
handleChange() {
wasCalled = true;
}
});

await Updates.next();

expect(wasCalled).to.be.false;
})
});

describe("The array length observer", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ class DefaultArrayObserver extends SubscriberSet implements ArrayObserver {
setNonEnumerable(subject, "$fastController", this);
}

public subscribe(subscriber: Subscriber): void {
this.flush();
super.subscribe(subscriber);
}

public addSplice(splice: Splice) {
if (this.splices === void 0) {
this.splices = [splice];
Expand Down
114 changes: 56 additions & 58 deletions packages/web-components/fast-element/src/observation/notifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,39 +49,6 @@ export interface Notifier {
unsubscribe(subscriber: Subscriber, propertyToUnwatch?: any): void;
}

const spilloverMethods = {
subscribe(this: SubscriberSet, subscriber: Subscriber): void {
const spillover = (this as any).spillover as Subscriber[];
const index = spillover.indexOf(subscriber);

if (index === -1) {
spillover.push(subscriber);
}
},

unsubscribe(this: SubscriberSet, subscriber: Subscriber): void {
const spillover = (this as any).spillover as Subscriber[];
const index = spillover.indexOf(subscriber);

if (index !== -1) {
spillover.splice(index, 1);
}
},

notify(this: SubscriberSet, args: any): void {
const spillover = (this as any).spillover as Subscriber[];
const subject = this.subject;

for (let i = 0, ii = spillover.length; i < ii; ++i) {
spillover[i].handleChange(subject, args);
}
},

has(this: SubscriberSet, subscriber: Subscriber): boolean {
return ((this as any).spillover as Subscriber[]).indexOf(subscriber) !== -1;
},
};

/**
* An implementation of {@link Notifier} that efficiently keeps track of
* subscribers interested in a specific change notification on an
Expand Down Expand Up @@ -118,43 +85,62 @@ export class SubscriberSet implements Notifier {
* @param subscriber - The subscriber to test for inclusion in this set.
*/
public has(subscriber: Subscriber): boolean {
return this.sub1 === subscriber || this.sub2 === subscriber;
return this.spillover === void 0
? this.sub1 === subscriber || this.sub2 === subscriber
: this.spillover.indexOf(subscriber) !== -1;
}

/**
* Subscribes to notification of changes in an object's state.
* @param subscriber - The object that is subscribing for change notification.
*/
public subscribe(subscriber: Subscriber): void {
if (this.has(subscriber)) {
return;
}
const spillover = this.spillover;

if (this.sub1 === void 0) {
this.sub1 = subscriber;
return;
}
if (spillover === void 0) {
if (this.has(subscriber)) {
return;
}

if (this.sub2 === void 0) {
this.sub2 = subscriber;
return;
}
if (this.sub1 === void 0) {
this.sub1 = subscriber;
return;
}

this.spillover = [this.sub1, this.sub2, subscriber];
Object.assign(this, spilloverMethods);
this.sub1 = void 0;
this.sub2 = void 0;
if (this.sub2 === void 0) {
this.sub2 = subscriber;
return;
}

this.spillover = [this.sub1, this.sub2, subscriber];
this.sub1 = void 0;
this.sub2 = void 0;
} else {
const index = spillover.indexOf(subscriber);
if (index === -1) {
spillover.push(subscriber);
}
}
}

/**
* Unsubscribes from notification of changes in an object's state.
* @param subscriber - The object that is unsubscribing from change notification.
*/
public unsubscribe(subscriber: Subscriber): void {
if (this.sub1 === subscriber) {
this.sub1 = void 0;
} else if (this.sub2 === subscriber) {
this.sub2 = void 0;
const spillover = this.spillover;

if (spillover === void 0) {
if (this.sub1 === subscriber) {
this.sub1 = void 0;
} else if (this.sub2 === subscriber) {
this.sub2 = void 0;
}
} else {
const index = spillover.indexOf(subscriber);
if (index !== -1) {
spillover.splice(index, 1);
}
}
}

Expand All @@ -163,12 +149,24 @@ export class SubscriberSet implements Notifier {
* @param args - Data passed along to subscribers during notification.
*/
public notify(args: any): void {
if (this.sub1 !== void 0) {
this.sub1.handleChange(this.subject, args);
}
const spillover = this.spillover;
const subject = this.subject;

if (this.sub2 !== void 0) {
this.sub2.handleChange(this.subject, args);
if (spillover === void 0) {
const sub1 = this.sub1;
const sub2 = this.sub2;

if (sub1 !== void 0) {
sub1.handleChange(subject, args);
}

if (sub2 !== void 0) {
sub2.handleChange(subject, args);
}
} else {
for (let i = 0, ii = spillover.length; i < ii; ++i) {
spillover[i].handleChange(subject, args);
}
}
}
}
Expand Down

0 comments on commit b0d1d70

Please sign in to comment.