Skip to content

Commit

Permalink
Add generatorWithLastValue
Browse files Browse the repository at this point in the history
  • Loading branch information
compulim committed Jun 13, 2024
1 parent cd25da6 commit f7f75be
Show file tree
Hide file tree
Showing 13 changed files with 589 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `readerValues` in PR [#12](https://github.com/compulim/iter-fest/pull/12) and [#14](https://github.com/compulim/iter-fest/pull/14)
- Added `observableSubscribeAsReadable` in PR [#13](https://github.com/compulim/iter-fest/pull/13)
- Added `iterableGetReadable` in PR [#15](https://github.com/compulim/iter-fest/pull/15)
- Added `generatorWithLastValue`/`asyncGeneratorWithLastValue` in PR [#XX](https://github.com/compulim/iter-fest/pull/XX)

### Changed

Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,44 @@ const iterable = new PushAsyncIterableIterator();
// Prints "1", "2", "3", "Done".
```

### Using for-loop with generator

Compare to `Iterator`, `Generator` offers advanced capability.

When using for-loop with generator, the last value return from the generator is lost.

The `generatorWithLastValue()` and `asyncGeneratorWithLastValue()` helps bridge the for-loop usage by storing the last value and make it accessible via `lastValue()`.

```ts
const generator = generatorWithLastValue(
(function* () {
yield 1;
yield 2;
yield 3;

return 'end';
})()
);

for (const value of generator) {
console.log(value); // Prints "1", "2", "3".
}

console.log(generator.lastValue()); // Prints "end".
```

Note: `lastValue()` will throw if it is being called before end of iteration.

The value returned from `generatorWithLastValue()`/`asyncGeneratorWithLastValue()` will passthrough all function calls to original `Generator` with a minor difference. Calling `[Symbol.iterator]()`/`[Symbol.asyncIterator]()` on the returned generator will not start a fresh iteration. If a fresh iteration is required, create a new one before passing it to `generatorWithLastValue()`/`asyncGeneratorWithLastValue()`.

```ts
const generator = generatorWithLastValue(
(function* () {
// ...
})()[Symbol.iterator]() // Creates a fresh iteration.
);
```

## Behaviors

### How this compares to the TC39 proposals?
Expand Down
34 changes: 34 additions & 0 deletions packages/integration-test/importDefault.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
Observable,
PushAsyncIterableIterator,
SymbolObservable,
asyncGeneratorWithLastValue,
generatorWithLastValue,
iterableAt,
iterableConcat,
iterableEntries,
Expand Down Expand Up @@ -30,6 +32,38 @@ import {
readerValues
} from 'iter-fest';

test('asyncGeneratorWithLastValue should work', async () => {
const asyncGenerator = asyncGeneratorWithLastValue(
(async function* () {
yield 1;

return 'end' as const;
})()
);

for await (const value of asyncGenerator) {
expect(value).toBe(1);
}

expect(asyncGenerator.lastValue()).toEqual('end');
});

test('generatorWithLastValue should work', () => {
const generator = generatorWithLastValue(
(function* () {
yield 1;

return 'end' as const;
})()
);

for (const value of generator) {
expect(value).toBe(1);
}

expect(generator.lastValue()).toEqual('end');
});

test('iterableAt should work', () => expect(iterableAt([1, 2, 3].values(), 1)).toBe(2));

test('iterableConcat should work', () =>
Expand Down
34 changes: 34 additions & 0 deletions packages/integration-test/importNamed.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import withResolvers from 'core-js-pure/full/promise/with-resolvers';
import { asyncGeneratorWithLastValue } from 'iter-fest/asyncGeneratorWithLastValue';
import { generatorWithLastValue } from 'iter-fest/generatorWithLastValue';
import { iterableAt } from 'iter-fest/iterableAt';
import { iterableConcat } from 'iter-fest/iterableConcat';
import { iterableEntries } from 'iter-fest/iterableEntries';
Expand Down Expand Up @@ -28,6 +30,38 @@ import { PushAsyncIterableIterator } from 'iter-fest/pushAsyncIterableIterator';
import { readerValues } from 'iter-fest/readerValues';
import { SymbolObservable } from 'iter-fest/symbolObservable';

test('asyncGeneratorWithLastValue should work', async () => {
const asyncGenerator = asyncGeneratorWithLastValue(
(async function* () {
yield 1;

return 'end' as const;
})()
);

for await (const value of asyncGenerator) {
expect(value).toBe(1);
}

expect(asyncGenerator.lastValue()).toEqual('end');
});

test('generatorWithLastValue should work', () => {
const generator = generatorWithLastValue(
(function* () {
yield 1;

return 'end' as const;
})()
);

for (const value of generator) {
expect(value).toBe(1);
}

expect(generator.lastValue()).toEqual('end');
});

test('iterableAt should work', () => expect(iterableAt([1, 2, 3].values(), 1)).toBe(2));

test('iterableConcat should work', () =>
Expand Down
34 changes: 34 additions & 0 deletions packages/integration-test/requireNamed.test.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const withResolvers = require('core-js-pure/full/promise/with-resolvers');

const { asyncGeneratorWithLastValue } = require('iter-fest/asyncGeneratorWithLastValue');
const { generatorWithLastValue } = require('iter-fest/generatorWithLastValue');
const { iterableAt } = require('iter-fest/iterableAt');
const { iterableConcat } = require('iter-fest/iterableConcat');
const { iterableEntries } = require('iter-fest/iterableEntries');
Expand Down Expand Up @@ -29,6 +31,38 @@ const { PushAsyncIterableIterator } = require('iter-fest/pushAsyncIterableIterat
const { readerValues } = require('iter-fest/readerValues');
const { SymbolObservable } = require('iter-fest/symbolObservable');

test('asyncGeneratorWithLastValue should work', async () => {
const asyncGenerator = asyncGeneratorWithLastValue(
(async function* () {
yield 1;

return 'end';
})()
);

for await (const value of asyncGenerator) {
expect(value).toBe(1);
}

expect(asyncGenerator.lastValue()).toEqual('end');
});

test('generatorWithLastValue should work', () => {
const generator = generatorWithLastValue(
(function* () {
yield 1;

return 'end';
})()
);

for (const value of generator) {
expect(value).toBe(1);
}

expect(generator.lastValue()).toEqual('end');
});

test('iterableAt should work', () => expect(iterableAt([1, 2, 3].values(), 1)).toBe(2));

test('iterableConcat should work', () =>
Expand Down
34 changes: 34 additions & 0 deletions packages/integration-test/requiredDefault.test.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const withResolvers = require('core-js-pure/full/promise/with-resolvers');

const {
asyncGeneratorWithLastValue,
generatorWithLastValue,
iterableAt,
iterableConcat,
iterableEntries,
Expand Down Expand Up @@ -31,6 +33,38 @@ const {
SymbolObservable
} = require('iter-fest');

test('asyncGeneratorWithLastValue should work', async () => {
const asyncGenerator = asyncGeneratorWithLastValue(
(async function* () {
yield 1;

return 'end';
})()
);

for await (const value of asyncGenerator) {
expect(value).toBe(1);
}

expect(asyncGenerator.lastValue()).toEqual('end');
});

test('generatorWithLastValue should work', () => {
const generator = generatorWithLastValue(
(function* () {
yield 1;

return 'end';
})()
);

for (const value of generator) {
expect(value).toBe(1);
}

expect(generator.lastValue()).toEqual('end');
});

test('iterableAt should work', () => expect(iterableAt([1, 2, 3].values(), 1)).toBe(2));

test('iterableConcat should work', () =>
Expand Down
20 changes: 20 additions & 0 deletions packages/iter-fest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@
"./dist/"
],
"exports": {
"./asyncGeneratorWithLastValue": {
"import": {
"types": "./dist/iter-fest.asyncGeneratorWithLastValue.d.mts",
"default": "./dist/iter-fest.asyncGeneratorWithLastValue.mjs"
},
"require": {
"types": "./dist/iter-fest.asyncGeneratorWithLastValue.d.ts",
"default": "./dist/iter-fest.asyncGeneratorWithLastValue.js"
}
},
"./generatorWithLastValue": {
"import": {
"types": "./dist/iter-fest.generatorWithLastValue.d.mts",
"default": "./dist/iter-fest.generatorWithLastValue.mjs"
},
"require": {
"types": "./dist/iter-fest.generatorWithLastValue.d.ts",
"default": "./dist/iter-fest.generatorWithLastValue.js"
}
},
"./iterableAt": {
"import": {
"types": "./dist/iter-fest.iterableAt.d.mts",
Expand Down
Loading

0 comments on commit f7f75be

Please sign in to comment.