/**
* Promise aware setTimeout based on Angulars method
* @param {Number} delay How long to wait before resolving
* @returns {Promise} A resolved Promise to signal the timeout is complete
*/
function $timeout(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), delay);
});
}
/**
* Return true (early) if any of the provided Promises are true
* @param {Function(arg: *): Boolean} predicate The test the resolved Promise value must pass to be considered true
* @param {Promise[]} arr The Promises to wait on
* @returns {Promise<Boolean>} Whether one of the the provided Promises passed the predicate
*/
async function some(predicate, arr) {
// Don't mutate arguemnts
const arrCopy = arr.slice(0);
// Wait until we run out of Promises
while(arrCopy.length){
// Give all our promises IDs so that we can remove them when they are done
const arrWithIDs = arrCopy.map((p, idx) => p.then(data => ({idx, data})).catch(_err => ({idx, data: false})));
// Wait for one of the Promises to resolve
const soon = await Promise.race(arrWithIDs);
// If it passes the test, we're done
if(predicate(soon.data))return true;
// Otherwise, remove that Promise and race again
arrCopy.splice(soon.idx, 1);
}
// No Promises passed the test
return false;
}
// Test harness
const tests = [
function allTrue(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => true),
$timeout(2000).then(() => true),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function twoSecondsTrue(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => false),
$timeout(2000).then(() => true),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function threeSecondsTrue(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => false),
$timeout(2000).then(() => false),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function allFalse(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => false),
$timeout(2000).then(() => false),
$timeout(3000).then(() => false)
]).then(d => {
console.log(d);
console.log(new Date());
});
},
function threeSecondsTrueWithError(){
console.log(new Date());
return some((v)=>v, [
$timeout(1000).then(() => { throw new Error() }),
$timeout(2000).then(() => false),
$timeout(3000).then(() => true)
]).then(d => {
console.log(d);
console.log(new Date());
});
}
]
tests.reduce((acc, curr) => acc.then(()=>curr()), Promise.resolve());