diff --git a/addons/web/static/src/core/utils/concurrency.js b/addons/web/static/src/core/utils/concurrency.js index 9fa431c121a..710dee25e66 100644 --- a/addons/web/static/src/core/utils/concurrency.js +++ b/addons/web/static/src/core/utils/concurrency.js @@ -123,6 +123,7 @@ export class Race { constructor() { this.currentProm = null; this.currentPromResolver = null; + this.currentPromRejecter = null; } /** * Register a new promise. If there is an ongoing race, the promise is added @@ -131,19 +132,26 @@ export class Race { * promise added to the race. * * @param {Promise} promise - * @returns {Promise + * @returns {Promise} */ add(promise) { if (!this.currentProm) { - this.currentProm = new Promise((resolve) => { + this.currentProm = new Promise((resolve, reject) => { this.currentPromResolver = (value) => { this.currentProm = null; this.currentPromResolver = null; + this.currentPromRejecter = null; resolve(value); }; + this.currentPromRejecter = (error) => { + this.currentProm = null; + this.currentPromResolver = null; + this.currentPromRejecter = null; + reject(error); + }; }); } - promise.then(this.currentPromResolver); + promise.then(this.currentPromResolver).catch(this.currentPromRejecter); return this.currentProm; } /** diff --git a/addons/web/static/tests/core/utils/concurrency_tests.js b/addons/web/static/tests/core/utils/concurrency_tests.js index 7b110510b34..6e67e68267c 100644 --- a/addons/web/static/tests/core/utils/concurrency_tests.js +++ b/addons/web/static/tests/core/utils/concurrency_tests.js @@ -302,6 +302,104 @@ QUnit.module("utils", () => { assert.verifySteps(["ok (44) [2]", "ok (44) [3]"]); }); + QUnit.test("Race: catch rejected promise", async function (assert) { + const race = new Race(); + const def = makeDeferred(); + + race.add(def).catch((v) => assert.step(`not ok (${v})`)); + + assert.verifySteps([]); + + def.reject(44); + await nextTick(); + + assert.verifySteps(["not ok (44)"]); + }); + + QUnit.test("Race: first promise rejects first", async function (assert) { + const race = new Race(); + const def1 = makeDeferred(); + const def2 = makeDeferred(); + + race.add(def1).catch((v) => assert.step(`not ok (${v}) [1]`)); + race.add(def2).catch((v) => assert.step(`not ok (${v}) [2]`)); + + assert.verifySteps([]); + + def1.reject(44); + await nextTick(); + + assert.verifySteps(["not ok (44) [1]", "not ok (44) [2]"]); + + def2.resolve(); + await nextTick(); + + assert.verifySteps([]); + }); + + QUnit.test("Race: second promise rejects after", async function (assert) { + const race = new Race(); + const def1 = makeDeferred(); + const def2 = makeDeferred(); + + race.add(def1).then((v) => assert.step(`ok (${v}) [1]`)); + race.add(def2).then((v) => assert.step(`ok (${v}) [2]`)); + + assert.verifySteps([]); + + def1.resolve(44); + await nextTick(); + + assert.verifySteps(["ok (44) [1]", "ok (44) [2]"]); + + def2.reject(); + await nextTick(); + + assert.verifySteps([]); + }); + + QUnit.test("Race: second promise rejects first", async function (assert) { + const race = new Race(); + const def1 = makeDeferred(); + const def2 = makeDeferred(); + + race.add(def1).catch((v) => assert.step(`not ok (${v}) [1]`)); + race.add(def2).catch((v) => assert.step(`not ok (${v}) [2]`)); + + assert.verifySteps([]); + + def2.reject(44); + await nextTick(); + + assert.verifySteps(["not ok (44) [1]", "not ok (44) [2]"]); + + def1.resolve(); + await nextTick(); + + assert.verifySteps([]); + }); + + QUnit.test("Race: first promise rejects after", async function (assert) { + const race = new Race(); + const def1 = makeDeferred(); + const def2 = makeDeferred(); + + race.add(def1).then((v) => assert.step(`ok (${v}) [1]`)); + race.add(def2).then((v) => assert.step(`ok (${v}) [2]`)); + + assert.verifySteps([]); + + def2.resolve(44); + await nextTick(); + + assert.verifySteps(["ok (44) [1]", "ok (44) [2]"]); + + def1.reject(); + await nextTick(); + + assert.verifySteps([]); + }); + QUnit.test("Race: getCurrentProm", async function (assert) { assert.expect(7);