[FIX] context: solve tricky concurrency issue

part of #330
This commit is contained in:
Géry Debongnie
2019-10-25 13:23:49 +02:00
parent 3c38bbc076
commit 690d8edf67
2 changed files with 25 additions and 10 deletions
+22 -7
View File
@@ -15,7 +15,7 @@ import { onWillUnmount } from "./hooks";
export class Context extends EventBus {
state: any;
observer: Observer;
id: number = 1;
rev: number = 1;
// mapping from component id to last observed context id
mapping: { [componentId: number]: number } = {};
@@ -46,13 +46,13 @@ export class Context extends EventBus {
* with the same depth in parallel.
*/
async __notifyComponents() {
const id = ++this.id;
const rev = ++this.rev;
const subs = this.subscriptions.update || [];
for (let i = 0, iLen = subs.length; i < iLen; i++) {
const sub = subs[i];
const shouldCallback = sub.owner ? sub.owner.__owl__.isMounted : true;
if (shouldCallback) {
const render = sub.callback.call(sub.owner, id);
const render = sub.callback.call(sub.owner, rev);
scheduler.flush();
await render;
}
@@ -76,15 +76,30 @@ export function useContextWithCB(ctx: Context, component: Component<any, any>, m
if (id in mapping) {
return ctx.state;
}
if (!__owl__.observer) {
__owl__.observer = new Observer();
__owl__.observer.notifyCB = component.render.bind(component);
}
const currentCB = __owl__.observer.notifyCB;
__owl__.observer.notifyCB = function () {
if (ctx.rev > mapping[id]) {
// in this case, the context has been updated since we were rendering
// last, and we do not need to render here with the observer. A
// rendering is coming anyway, with the correct props.
return;
}
currentCB();
}
mapping[id] = 0;
const renderFn = __owl__.renderFn;
__owl__.renderFn = function(comp, params) {
mapping[id] = ctx.id;
mapping[id] = ctx.rev;
return renderFn(comp, params);
};
ctx.on("update", component, async contextId => {
if (mapping[id] < contextId) {
mapping[id] = contextId;
ctx.on("update", component, async contextRev => {
if (mapping[id] < contextRev) {
mapping[id] = contextRev;
await method();
}
});
+3 -3
View File
@@ -174,7 +174,7 @@ describe("Context", () => {
expect(testContext.subscriptions.update.length).toBe(0);
});
test.skip("concurrent renderings", async () => {
test("concurrent renderings", async () => {
const testContext = new Context({ x: { n: 1 }, key: "x" });
const def = makeDeferred();
let stateC;
@@ -207,7 +207,7 @@ describe("Context", () => {
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
testContext.state.key = "y";
testContext.state.y = 2;
testContext.state.y = {n: 2};
delete testContext.state.x;
await nextTick();
@@ -219,6 +219,6 @@ describe("Context", () => {
def.resolve();
await nextTick();
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
expect(fixture.innerHTML).toBe("<div><p><span>2b</span></p></div>");
});
});