-
Matiss Janis Aboltins authoredMatiss Janis Aboltins authored
async.test.ts 3.46 KiB
// @ts-strict-ignore
import { sequential, once } from './async';
function timeout(n) {
return new Promise(resolve => setTimeout(resolve, n));
}
function makeFunction(data) {
return async function fn(n, { throwError = false } = {}) {
data.push(n);
await timeout(10);
if (throwError) {
throw new Error('throwing error');
}
data.push(n);
await timeout(50);
data.push(n);
};
}
describe('async', () => {
test('sequential fn should force concurrent calls to be in order', async () => {
const test = async fn => {
fn(1);
fn(2);
await fn(3);
};
const data = [];
await test(makeFunction(data));
expect(data).toEqual([1, 2, 3, 1, 2, 3, 1, 2, 3]);
const seqData = [];
await test(sequential(makeFunction(seqData)));
expect(seqData).toEqual([1, 1, 1, 2, 2, 2, 3, 3, 3]);
expect(data.length).toEqual(seqData.length);
});
test('sequential fn should always call function when queue is empty', async () => {
const test = async fn => {
await fn(1);
await fn(2);
await fn(3);
};
const data = [];
await test(makeFunction(data));
expect(data).toEqual([1, 1, 1, 2, 2, 2, 3, 3, 3]);
const seqData = [];
await test(sequential(makeFunction(seqData)));
expect(seqData).toEqual([1, 1, 1, 2, 2, 2, 3, 3, 3]);
expect(data.length).toEqual(seqData.length);
});
test('sequential fn should still flush queue when error is thrown', async () => {
const test = async fn => {
fn(1);
fn(2, { throwError: true }).catch(() => {});
await fn(3);
};
const data = [];
await test(makeFunction(data));
expect(data).toEqual([1, 2, 3, 1, 3, 1, 3]);
const seqData = [];
await test(sequential(makeFunction(seqData)));
expect(seqData).toEqual([1, 1, 1, 2, 3, 3, 3]);
expect(data.length).toEqual(seqData.length);
});
test('sequential fn should ignore promise chains in the future', async () => {
const data = [];
const fn = sequential(makeFunction(data));
fn(1).then(() => {
// The next call should already have started (so it should have
// already appended 2 to the end). It shouldn't depend on this
// promise chain at all (important part being that if any errors
// happened in here, it wouldn't effect anything else)
expect(data).toEqual([1, 1, 1, 2]);
});
fn(2, { throwError: true }).catch(() => {
// Same as above
expect(data).toEqual([1, 1, 1, 2, 3]);
});
await fn(3);
expect(data).toEqual([1, 1, 1, 2, 3, 3, 3]);
});
test('once fn should only be called once', async () => {
let timesCalled = 0;
const fn = once(async () => {
await timeout(200);
timesCalled++;
});
await Promise.all([fn(), fn(), fn()]);
// It should only have been called once
expect(timesCalled).toBe(1);
// Make sure it's called again now that it's done executing
await Promise.all([fn(), fn()]);
expect(timesCalled).toBe(2);
});
test('once fn should coalesce multiple calls', async () => {
let timesCalled = 0;
const fn = once(async () => {
await timeout(200);
timesCalled++;
return {};
});
const results = await Promise.all([fn(), fn(), fn()]);
// It should only have been called once
expect(timesCalled).toBe(1);
// The results should all be identical (`toBe` is a strict
// comparison, like ===)
expect(results[0]).toBe(results[1]);
expect(results[0]).toBe(results[2]);
});
});