Skip to content

Commit

Permalink
Merge pull request #46 from sebastiendavid/cancel-returned
Browse files Browse the repository at this point in the history
Cancel cancelable promise returned by a then/catch callback
  • Loading branch information
sebdvd authored Jun 16, 2021
2 parents 321e6be + a7aaa6d commit b02c4d2
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 8 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@
"prepublish": "npm run build",
"prettier:rc": "prettier --write",
"prettier": "prettier --write .",
"release:major:nc": "node scripts.js prepare_release major --no-commit",
"release:major": "node scripts.js prepare_release major",
"release:minor:nc": "node scripts.js prepare_release minor --no-commit",
"release:minor": "node scripts.js prepare_release minor",
"release:patch:nc": "node scripts.js prepare_release patch --no-commit",
"release:patch": "node scripts.js prepare_release patch",
"test:e2e": "concurrently 'npm run test:serve' 'wait-on http://localhost:3000/esm.html && npm run cypress' --kill-others --success first",
"test:serve": "node tests/esm.server.js",
Expand Down
16 changes: 9 additions & 7 deletions scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ ${commits}
${changelog}`;
await fs.writeFile('CHANGELOG.md', changelog, { encoding: 'utf8' });
await exec('git add CHANGELOG.md');
await exec(
`git commit -m "[RELEASE] update changelog for v${newVersion}" --no-verify`
);
await exec(
`npm version --no-commit-hooks ${newVersion} -m '[RELEASE] v${newVersion}'`
);
if (!process.argv.includes('--no-commit')) {
await exec('git add CHANGELOG.md');
await exec(
`git commit -m "[RELEASE] update changelog for v${newVersion}" --no-verify`
);
await exec(
`npm version --no-commit-hooks ${newVersion} -m '[RELEASE] v${newVersion}'`
);
}
}

runScript().catch((err) => {
Expand Down
9 changes: 8 additions & 1 deletion src/CancelablePromise.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ function createCallback(onResult, options) {
if (onResult) {
return (arg) => {
if (!options.isCanceled) {
return onResult(arg);
const result = onResult(arg);
if (result && typeof result.cancel === 'function') {
if (!options.onCancelList) {
options.onCancelList = [];
}
options.onCancelList.push(result.cancel);
}
return result;
}
return arg;
};
Expand Down
203 changes: 203 additions & 0 deletions tests/CancelablePromise.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,206 @@ describe('CancelablePromise.race()', () => {
expect(callback).toHaveBeenCalledTimes(0);
});
});

describe('Cancelable promises returned by executors', () => {
async function worflow({ withClass, withFail, withCatch }) {
const callback = jest.fn();
let promise1;

if (withClass) {
promise1 = new CancelablePromise((resolve, reject, onCancel) => {
callback('start p1');
const timer = setTimeout(() => {
callback('resolve p1');
if (withFail) {
reject();
} else {
resolve();
}
}, 5);
const abort = () => {
callback('abort p1');
clearTimeout(timer);
};
onCancel(abort);
});
} else {
promise1 = cancelable(
new Promise((resolve, reject) => {
callback('start p1');
delay(5, () => {
callback('resolve p1');
if (withFail) {
reject();
} else {
resolve();
}
});
})
);
}

let promise2 = promise1.then(
...[
() => {
callback('then p2');
const promise3 = new CancelablePromise(
(resolve, reject, onCancel) => {
callback('start p3');
const timer = setTimeout(() => {
callback('resolve p3');
resolve();
}, 10);
const abort = () => {
callback('abort p3');
clearTimeout(timer);
};
onCancel(abort);
}
);
return promise3;
},
!withCatch &&
(() => {
callback('error p2');
const promise3 = new CancelablePromise(
(resolve, reject, onCancel) => {
callback('start p3');
const timer = setTimeout(() => {
callback('resolve p3');
resolve();
}, 10);
const abort = () => {
callback('abort p3');
clearTimeout(timer);
};
onCancel(abort);
}
);
return promise3;
}),
].filter(Boolean)
);

if (withCatch) {
promise2 = promise2.catch(() => {
callback('catch p2');
const promise3 = new CancelablePromise((resolve, reject, onCancel) => {
callback('start p3');
const timer = setTimeout(() => {
callback('resolve p3');
resolve();
}, 10);
const abort = () => {
callback('abort p3');
clearTimeout(timer);
};
onCancel(abort);
});
return promise3;
});
}

promise2.then(() => {
callback('then done');
});

await delay(10, () => {
callback('cancel p2');
promise2.cancel();
});
await delay(20);
return callback;
}

it('should be canceled when fulfilled (with CancelablePromise)', async () => {
const callback = await worflow({ withClass: true, withFail: false });
expect(callback.mock.calls).toEqual([
['start p1'],
['resolve p1'],
['then p2'],
['start p3'],
['cancel p2'],
['abort p1'],
['abort p3'],
]);
});

it('should be canceled when rejected (with CancelablePromise)', async () => {
const callback = await worflow({
withClass: true,
withFail: true,
withCatch: false,
});
expect(callback.mock.calls).toEqual([
['start p1'],
['resolve p1'],
['error p2'],
['start p3'],
['cancel p2'],
['abort p1'],
['abort p3'],
]);
});

it('should be canceled when rejected and caught (with CancelablePromise)', async () => {
const callback = await worflow({
withClass: true,
withFail: true,
withCatch: true,
});
expect(callback.mock.calls).toEqual([
['start p1'],
['resolve p1'],
['catch p2'],
['start p3'],
['cancel p2'],
['abort p1'],
['abort p3'],
]);
});

it('should be canceled when fulfilled (with cancelable)', async () => {
const callback = await worflow({ withClass: false, withFail: false });
expect(callback.mock.calls).toEqual([
['start p1'],
['resolve p1'],
['then p2'],
['start p3'],
['cancel p2'],
['abort p3'],
]);
});

it('should be canceled when rejected (with cancelable)', async () => {
const callback = await worflow({
withClass: false,
withFail: true,
withCatch: false,
});
expect(callback.mock.calls).toEqual([
['start p1'],
['resolve p1'],
['error p2'],
['start p3'],
['cancel p2'],
['abort p3'],
]);
});

it('should be canceled when rejected and caught (with cancelable)', async () => {
const callback = await worflow({
withClass: false,
withFail: true,
withCatch: true,
});
expect(callback.mock.calls).toEqual([
['start p1'],
['resolve p1'],
['catch p2'],
['start p3'],
['cancel p2'],
['abort p3'],
]);
});
});

0 comments on commit b02c4d2

Please sign in to comment.