[FIX] web: reject race promise when first promise rejects

The Race concurrency promise never settles if the first settled promise
rejects and therefore you can't catch the rejected promise.

closes odoo/odoo#104733

X-original-commit: 24cc8cdb440a5af944d82a25d98b0239e72e2625
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Lucas Lefèvre (lul) <lul@odoo.com>
This commit is contained in:
Lucas Lefèvre
2022-11-02 15:34:41 +01:00
parent f66fa9433f
commit 080160de54
2 changed files with 109 additions and 3 deletions
@@ -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;
}
/**
@@ -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);