February 18, 2021
TestsSource
- Promises - How to collect more than one promise (in a loop?)
- What is Promise.any?
- How does Promise.prototype.finally work?
Source Code
The source code we wrote during the event.
index.test.js
// Source at: https://codeberg.org/wolframkriesing/jslang-meetups
import {strict as assert} from 'assert';
describe('Promises - How to collect more than one promise (in a loop?)', () => {
it('the value of one resolved promises can be retreived', () => {
return Promise.resolve(5)
.then((value) => {
assert.equal(value, 5);
});
});
it('the value of one resolved promises can be retreived (testing mocha)', (done) => {
Promise.resolve(5)
.then((value) => {
assert.equal(value, 5);
done();
});
});
it('collect two promises using Promise.all()', () => {
return Promise.all([
Promise.resolve(5),
Promise.resolve(10),
]).then((values) => {
assert.deepEqual(values, [5, 10]);
})
});
it('Promise.all() keeps the order independent of the timing', () => {
return Promise.all([
new Promise((resolve) => { setTimeout(() => resolve(5), 10) }),
Promise.resolve(10),
]).then((values) => {
assert.deepEqual(values, [5, 10]);
})
});
});
describe('What is Promise.any?', () => {
it('one resolving promise returns the value', () => {
const onePromise = [Promise.resolve(5)];
return Promise.any(onePromise).then((value) => {
assert.equal(value, 5);
});
});
it('no promise in Promise.any() rejects', () => {
const noPromises = [];
return Promise.any(noPromises)
.catch(error => {
assert(error instanceof AggregateError);
});
});
it('one rejected promise rejects with an AggregateError and an array `errors` with one entry', () => {
return Promise.any([
Promise.reject('hi 5')
]).catch((error) => {
assert(error instanceof AggregateError);
assert.deepEqual(error.errors, ['hi 5']);
})
});
it('two rejected and one later resolved promise, will return the first resolved one', () => {
return Promise.any([
Promise.reject('1'),
Promise.reject('2'),
new Promise((resolve) => { setTimeout(() => resolve('ok'), 100); }),
]).then((value) => {
assert.equal(value, 'ok');
})
});
it('does NOT abort the execution of other promises once the first one resolves (see the 100ms test run time)', () => {
const promise1 = new Promise((resolve) => { setTimeout(() => resolve('I am 1'), 15) });
const promise2 = new Promise((resolve) => { setTimeout(() => resolve('I am 2'), 100) });
return Promise.any([
promise1,
promise2
]).then((result) => {
assert.equal(result, 'I am 1');
return promise2;
});
});
it('two rejected and two later resolved promises, will return the first one', () => {
return Promise.any([
Promise.reject('1'),
new Promise((resolve) => { setTimeout(() => resolve('ok 1'), 100); }),
Promise.reject(7),
new Promise((resolve) => { setTimeout(() => resolve('ok 2'), 50); }),
]).then((value) => {
assert.equal(value, 'ok 2');
})
});
xit('two racing promises, will return the first one', () => {
return Promise.any([
new Promise((resolve) => { setTimeout(() => resolve('ok 1'), 51); }),
new Promise((resolve) => { setTimeout(() => resolve('ok 2'), 50); }),
]).then((value) => {
assert.equal(value, 'ok 2');
})
});
xit('FLAKY: two racy promise creations', () => {
const a = new Promise((resolve) => { setTimeout(() => resolve('ok 1'), 51); });
// just a loop to "make it slow"
let count = 1;
for (let i=0; i<1000; i++) {
count = i * Math.random();
}
const b = new Promise((resolve) => { setTimeout(() => resolve('ok 2'), 50); });
return Promise.any([a, b]).then((value) => {
assert.equal(value, 'ok 2');
})
});
it('does NOT work with a number as parameter', () => {
return Promise.any(1)
.catch((e) => {
assert(e instanceof TypeError);
})
});
it('an array of numbers returns the first number', () => {
return Promise.any([1,2,3])
.then(firstNumber => {
assert.equal(firstNumber, 1);
})
});
});
describe('How does Promise.prototype.finally work?', () => {
it('will be called after a promise resolved', () => {
return new Promise((resolve) => {
Promise
.resolve(1)
.finally(() => {
resolve();
});
});
});
it('will be called after a rejected promise was handled', () => {
const rejectedPromise = Promise.reject(Error(''));
return new Promise((resolve) => {
rejectedPromise.catch(() => {}).finally(() => {
resolve();
});
});
});
it('will be called is rejected', () => {
const rejectedPromise = Promise.reject(Error(''));
return new Promise((resolve) => {
rejectedPromise.finally(() => {
resolve();
}).catch(() => {});
});
});
it('inside finally() we throw then catch() after it in the promise chain catches it', () => {
const resolvedPromise = Promise.resolve();
return new Promise((resolve) => {
resolvedPromise.finally(() => {
throw Error('');
}).catch(() => {
resolve();
});
});
});
it('finally can be chained', () => {
const resolvedPromise = Promise.resolve();
return new Promise((resolve) => {
resolvedPromise.finally(() => {
throw Error('');
}).catch(() => {
}).finally(() => {
throw Error('');
}).catch(() => {
resolve();
});
});
});
it('finally can be chained on finally', () => {
const resolvedPromise = Promise.resolve();
return new Promise((resolve) => {
resolvedPromise
.finally(() => {})
.finally(() => {})
.finally(() => {})
.finally(() => {})
.finally(resolve)
;
});
});
it('an error bubbles up the chain in finally', () => {
const resolvedPromise = Promise.resolve();
return new Promise((resolve) => {
resolvedPromise
.finally(() => {
throw Error('42');
}).catch((e) => {
assert.equal(e.message, '42');
resolve();
});
});
});
it('a rejected promise does fall through a finally', async () => {
const rejectedPromise = Promise.reject();
const result = await rejectedPromise
.finally(() => { return 'in finally'; })
.catch(() => { return 'in catch'; });
assert.equal(result, 'in catch');
});
it('finally after a rejected promise is executed and shadows the actual rejection', async () => {
const rejectedPromise = Promise.reject('rejected promise')
.finally(() => { throw Error('finally was run'); })
.catch((error) => { return error.message; })
;
const result = await rejectedPromise;
assert.equal(result, 'finally was run');
});
it('value from finally() does NOT override value from rejected promise', async () => {
const rejectedPromise = Promise.reject('rejected')
.finally(() => { return 'in finally'; })
.catch((error) => { return error; });
assert.equal(await rejectedPromise, 'rejected');
});
it('result of a rejected promise is NOT an error', () => {
return Promise.reject('a string')
.catch((error) => {
assert.equal(error, 'a string');
});
});
it('awaiting a Promise.reject throws its parameter', async () => {
try {
await Promise.reject('my param');
} catch (e) {
assert.equal(e, 'my param');
}
});
it('a rejected value in finally is NOT retreivable by a then() after it', async () => {
const breadcrumbs = [];
const rejectedPromise = Promise.reject('rejected')
.finally((value) => {
breadcrumbs.push('in finally()');
breadcrumbs.push(value);
return 'from finally';
}).then((value) => {
breadcrumbs.push('in then()');
breadcrumbs.push(value);
}).catch((error) => {
breadcrumbs.push('in catch()');
breadcrumbs.push(error);
})
;
await rejectedPromise;
assert.deepEqual(breadcrumbs, ['in finally()', undefined, 'in catch()', 'rejected']);
});
it('a resolved value in finally is NOT retreivable by a then() after it', async () => {
const breadcrumbs = [];
const rejectedPromise = Promise.resolve('resolved')
.finally((value) => {
breadcrumbs.push('in finally()');
breadcrumbs.push(value);
return 'from finally';
}).then((value) => {
breadcrumbs.push('in then()');
breadcrumbs.push(value);
}).catch((error) => {
breadcrumbs.push('in catch()');
breadcrumbs.push(error);
})
;
await rejectedPromise;
assert.deepEqual(breadcrumbs, ['in finally()', undefined, 'in then()', 'resolved']);
});
xit('A delayed Promise.reject WILL fail the test', () => {
setTimeout(() => Promise.reject(Error('')), 1);
});
});