Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Jul 8, 2023
1 parent b1ac70a commit f3fb4cc
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
36 changes: 36 additions & 0 deletions TODO.md
@@ -0,0 +1,36 @@
# TODO

* AbortError subclass for abort due to collection completion?
* Tests for Collections awaiting all promises if iteration error and `await` option set
* Collections handle where getting `.abort` or `[IS_ABORTABLE]` properties throws error
* Collections handle where calling `.abort()` throws error
* `toAbortable()` handle where getting `[IS_ABORTABLE]` property throws error
* Simplify handling internal then calls?
* `.abort()` called on promise directly unilaterally abort, regardless of number of `.then()` calls
* `.finally` method

* Deal with case:

```js
const pInner = new Abortable((resolve, reject, onAbort) => {
onAbort((err) => {
console.log('aborted');
reject(err);
});
});

let p2, pThen;
const p1 = new Abortable((resolve1) => {
p2 = new Abortable((resolve2) => {
resolve1(pInner);
Promise.resolve().then(() => pThen = pInner.then(v => v));
resolve2(pInner);
});
});

p1.abort();
p2.abort();
pThen.abort();
// `pInner` should abort at this point, but I think it won't due to `pInner.then()` being wrongly
// identified as an internal `.then()` call
```
38 changes: 38 additions & 0 deletions foo.js
@@ -0,0 +1,38 @@
/* eslint-disable no-console */

'use strict';

const Abortable = require('./index.js');

let resolve;
const p = Abortable.all([{
then(_resolve) {
resolve = _resolve;
}
}]);

(async () => {
await Promise.resolve();

let thenGetterCalled = false;
let thenCalled = false;
resolve({
get then() {
thenGetterCalled = true;
return (r) => {
thenCalled = true;
r(123);
};
}
});

console.log('thenGetterCalled sync:', thenGetterCalled);
console.log('thenCalled sync:', thenCalled);

await Promise.resolve().then(() => {
console.log('thenCalled after one microtick:', thenCalled);
});

const res = await p;
console.log('res:', res);
})();
108 changes: 108 additions & 0 deletions test/promise.test.js
@@ -0,0 +1,108 @@
/* --------------------
* abortable module
* Tests for behavior of native JS Promise
* ------------------*/

'use strict';

// Imports
const {tick} = require('./support/utils.js');

// Init
require('./support/index.js');

// Tests

class PromiseExtended extends Promise {
constructor(executor) {
super(executor || (() => {}));
this.thenCalledTimes = 0;
}

then() {
this.thenCalledTimes++;
}
}

describe('Native JS Promise', () => {
describe('calls `.then()` asynchronously on promise passed to `resolve()`', () => {
describe('`resolve()` called synchronously in executor', () => {
it('of native Promise', async () => {
const p = new PromiseExtended();
new Promise(resolve => resolve(p)); // eslint-disable-line no-new
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});

it('of extended Promise', async () => {
const p = new PromiseExtended();
new PromiseExtended(resolve => resolve(p)); // eslint-disable-line no-new
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});
});

describe('`resolve()` called synchronously after construction of Promise', () => {
it('of native Promise', async () => {
let resolve;
new Promise((_resolve) => { resolve = _resolve; }); // eslint-disable-line no-new
const p = new PromiseExtended();
resolve(p);
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});

it('of extended Promise', async () => {
let resolve;
new PromiseExtended((_resolve) => { resolve = _resolve; }); // eslint-disable-line no-new
const p = new PromiseExtended();
resolve(p);
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});
});

describe('`resolve()` called asynchronously', () => {
it('of native Promise', async () => {
let resolve;
new Promise((_resolve) => { resolve = _resolve; }); // eslint-disable-line no-new
await tick();
const p = new PromiseExtended();
resolve(p);
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});

it('of extended Promise', async () => {
let resolve;
new PromiseExtended((_resolve) => { resolve = _resolve; }); // eslint-disable-line no-new
await tick();
const p = new PromiseExtended();
resolve(p);
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});
});
});

// eslint-disable-next-line jest/no-disabled-tests
describe.skip('calls `.then()` asynchronously on promise returned from `.then()` handler', () => {
it('foo', async () => {
let p;
Promise.resolve().then(() => {
p = new PromiseExtended();
return p;
});
await tick();
expect(p.thenCalledTimes).toBe(0);
await tick();
expect(p.thenCalledTimes).toBe(1);
});
});
});

0 comments on commit f3fb4cc

Please sign in to comment.