Skip to content

Commit

Permalink
feat(never): Adds never
Browse files Browse the repository at this point in the history
  • Loading branch information
mpodwysocki authored and trxcllnt committed Sep 1, 2020
1 parent c9d9f1e commit 64a9c31
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 3 deletions.
104 changes: 104 additions & 0 deletions spec/asynciterable/generatetime-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { hasNext, noNext } from '../asynciterablehelpers';
import { generateTime } from 'ix/asynciterable';

test('AsyncIterable#generateTime generateTimes normal sequence', async () => {
const xs = generateTime(
0,
async (x) => x < 5,
async (x) => x + 1,
async (x) => x * x,
async (x) => x * 100
);

const it = xs[Symbol.asyncIterator]();
await hasNext(it, 0);
await hasNext(it, 1);
await hasNext(it, 4);
await hasNext(it, 9);
await hasNext(it, 16);
await noNext(it);
});

test('AsyncIterable#generateTime condition throws', async () => {
const err = new Error();
const xs = generateTime(
0,
async (_) => {
throw err;
},
async (x) => x + 1,
async (x) => x * x,
async (x) => x * 100
);

const it = xs[Symbol.asyncIterator]();

try {
await it.next();
} catch (e) {
expect(err).toEqual(e);
}
});

test('AsyncIterable#generateTime increment throws', async () => {
const err = new Error();
const xs = generateTime(
0,
async (x) => x < 5,
async (_) => {
throw err;
},
async (x) => x * x,
async (x) => x * 100
);

const it = xs[Symbol.asyncIterator]();

try {
await it.next();
} catch (e) {
expect(err).toEqual(e);
}
});

test('AsyncIterable#generateTime result selector throws', async () => {
const err = new Error();
const xs = generateTime(
0,
async (x) => x < 5,
async (x) => x + 1,
async (_) => {
throw err;
},
async (x) => x * 100
);

const it = xs[Symbol.asyncIterator]();

try {
await it.next();
} catch (e) {
expect(err).toEqual(e);
}
});

test('AsyncIterable#generateTime time selector throws', async () => {
const err = new Error();
const xs = generateTime(
0,
async (x) => x < 5,
async (x) => x + 1,
async (x) => x * x,
async (_) => {
throw err;
}
);

const it = xs[Symbol.asyncIterator]();

try {
await it.next();
} catch (e) {
expect(err).toEqual(e);
}
});
13 changes: 13 additions & 0 deletions spec/asynciterable/never-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { never } from 'ix/asynciterable';

test('AsyncIterable#of never', async () => {
let called = false;
const res = never();

const it = res[Symbol.asyncIterator]();
it.next().then(() => {
called = true;
});

expect(called).toBeFalsy();
});
12 changes: 12 additions & 0 deletions src/add/asynciterable/generatetime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { AsyncIterableX } from '../../asynciterable/asynciterablex';
import { generateTime as generateTimeStatic } from '../../asynciterable/generatetime';

/** @nocollapse */
AsyncIterableX.generateTime = generateTimeStatic;

declare module '../../asynciterable/asynciterablex' {
// eslint-disable-next-line no-shadow
namespace AsyncIterableX {
export let generateTime: typeof generateTimeStatic;
}
}
12 changes: 12 additions & 0 deletions src/add/asynciterable/never.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { AsyncIterableX } from '../../asynciterable/asynciterablex';
import { never as neverStatic } from '../../asynciterable/never';

/** @nocollapse */
AsyncIterableX.never = neverStatic;

declare module '../../asynciterable/asynciterablex' {
// eslint-disable-next-line no-shadow
namespace AsyncIterableX {
export let never: typeof neverStatic;
}
}
53 changes: 53 additions & 0 deletions src/asynciterable/generatetime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { AsyncIterableX } from './asynciterablex';
import { sleep } from './_sleep';

class GenerateTimeAsyncIterable<TState, TResult> extends AsyncIterableX<TResult> {
private _initialState: TState;
private _condition: (value: TState, signal?: AbortSignal) => boolean | Promise<boolean>;
private _iterate: (value: TState, signal?: AbortSignal) => TState | Promise<TState>;
private _resultSelector: (value: TState, signal?: AbortSignal) => TResult | Promise<TResult>;
private _timeSelector: (value: TState, signal?: AbortSignal) => number | Promise<number>;

constructor(
initialState: TState,
condition: (value: TState, signal?: AbortSignal) => boolean | Promise<boolean>,
iterate: (value: TState, signal?: AbortSignal) => TState | Promise<TState>,
resultSelector: (value: TState, signal?: AbortSignal) => TResult | Promise<TResult>,
timeSelector: (value: TState, signal?: AbortSignal) => number | Promise<number>
) {
super();
this._initialState = initialState;
this._condition = condition;
this._iterate = iterate;
this._resultSelector = resultSelector;
this._timeSelector = timeSelector;
}

async *[Symbol.asyncIterator](signal?: AbortSignal) {
for (
let i = this._initialState;
await this._condition(i, signal);
i = await this._iterate(i, signal)
) {
const time = await this._timeSelector(i, signal);
await sleep(time, signal);
yield await this._resultSelector(i, signal);
}
}
}

export function generateTime<TState, TResult>(
initialState: TState,
condition: (value: TState, signal?: AbortSignal) => boolean | Promise<boolean>,
iterate: (value: TState, signal?: AbortSignal) => TState | Promise<TState>,
resultSelector: (value: TState, signal?: AbortSignal) => TResult | Promise<TResult>,
timeSelector: (value: TState, signal?: AbortSignal) => number | Promise<number>
): AsyncIterableX<TResult> {
return new GenerateTimeAsyncIterable<TState, TResult>(
initialState,
condition,
iterate,
resultSelector,
timeSelector
);
}
2 changes: 2 additions & 0 deletions src/asynciterable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './fromeventpattern';
export * from './fromevent';
export * from './from';
export * from './generate';
export * from './generatetime';
export * from './iif';
export * from './includes';
export * from './interval';
Expand All @@ -30,6 +31,7 @@ export * from './last';
export * from './max';
export * from './merge';
export * from './min';
export * from './never';
export * from './of';
export * from './onerrorresumenext';
export * from './pipe';
Expand Down
20 changes: 20 additions & 0 deletions src/asynciterable/never.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AsyncIterableX } from './asynciterablex';
import { throwIfAborted } from '../aborterror';

// eslint-disable-next-line @typescript-eslint/no-empty-function
const NEVER_PROMISE = new Promise<never>(() => {});

export class NeverAsyncIterable extends AsyncIterableX<never> {
constructor() {
super();
}

async *[Symbol.asyncIterator](signal?: AbortSignal) {
throwIfAborted(signal);
await NEVER_PROMISE;
}
}

export function never(): AsyncIterableX<never> {
return new NeverAsyncIterable();
}
5 changes: 5 additions & 0 deletions src/asynciterable/operators/tap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PartialAsyncObserver } from '../../observer';
import { MonoTypeOperatorAsyncFunction } from '../../interfaces';
import { toObserver } from '../../util/toobserver';
import { wrapWithAbort } from './withabort';
import { AbortError } from '../../aborterror';

export class TapAsyncIterable<TSource> extends AsyncIterableX<TSource> {
private _source: AsyncIterable<TSource>;
Expand All @@ -22,6 +23,10 @@ export class TapAsyncIterable<TSource> extends AsyncIterableX<TSource> {
try {
next = await it.next();
} catch (e) {
if (e instanceof AbortError) {
throw e;
}

if (this._observer.error) {
await this._observer.error(e);
}
Expand Down
30 changes: 27 additions & 3 deletions src/asynciterable/repeatvalue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
import { of } from './of';
import { RepeatAsyncIterable } from './operators/repeat';
import { AsyncIterableX } from './asynciterablex';
import { throwIfAborted } from 'ix/aborterror';

export class RepeatValueAsyncIterable<TSource> extends AsyncIterableX<TSource> {
private _value: TSource;
private _count: number;

constructor(value: TSource, count: number) {
super();
this._value = value;
this._count = count;
}

async *[Symbol.asyncIterator](signal?: AbortSignal) {
if (this._count === -1) {
while (1) {
throwIfAborted(signal);
yield this._value;
}
} else {
for (let i = 0; i < this._count; i++) {
throwIfAborted(signal);
yield this._value;
}
}
}
}

export function repeatValue<TSource>(value: TSource, count: number = -1): AsyncIterableX<TSource> {
return new RepeatAsyncIterable<TSource>(of(value), count);
return new RepeatValueAsyncIterable<TSource>(value, count);
}

0 comments on commit 64a9c31

Please sign in to comment.