Skip to content

Instantly share code, notes, and snippets.

@James-Firth
Last active April 20, 2022 00:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save James-Firth/9a516e2f506c7a76d8577035e7226826 to your computer and use it in GitHub Desktop.
Save James-Firth/9a516e2f506c7a76d8577035e7226826 to your computer and use it in GitHub Desktop.
Demonstrates errors handling arrays of promises

Typescript and Javascript Examples

This will have examples in a file grouped by topic. These are FAQs from co-workers, etc.

/*
This is a demo file to show how async/await works when iterating through a list of calls
*/
// ===== SETUP AND FAKE API ======
// These functions represent functions that take time to return like API/Network calls
function fakeApiCall(message, timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`\t\t`, new Date(), message);
resolve();
}, timeout);
});
}
const zerothCall = () => {
return fakeApiCall('ZERO', 5000);
};
const firstCall = () => {
return fakeApiCall('ONE', 3000);
};
const secondCall = () => {
return fakeApiCall('TWO', 1000);
};
// Put them in an array so we can call them all
const apiCallsToMake = [zerothCall, firstCall, secondCall];
// ====== BROKEN (on purpose) ======
async function doesNotWorkAsIntended() {
console.log('=== START OF BAD 1 ===');
// Nothing is causing us to wait for runMe (ie each API call) to complete
// so it'll just run through all of them!
apiCallsToMake.forEach((runMe, index) => {
console.log(`\t(bad) call start ${index}`);
runMe();
console.log(`\t(bad) call end ${index}`);
});
console.log('=== DONE BAD 1 ===');
}
// The problem is nothing is waiting for the forEach (which returns void)
// Not even the await, that's only causing
async function stillDoesNotWorkAsIntended() {
console.log('=== START BAD ASYNC ====');
// Nothing is causing us to wait for each of the calls within, as forEach just keeps marching on
apiCallsToMake.forEach(async (runMe, index) => {
console.log(`\t(bad) call start ${index}`);
await runMe();
console.log(`\t(bad) call end ${index}`);
});
console.log('=== DONE BAD ASYNC ====');
}
// ==== WORKING =====
async function worksAsIntended() {
console.log('=== START GOOD 1 ===');
const promisesForAllTheCalls = apiCallsToMake.map(async (runMe, index) => {
console.log(`\t(good) call start ${index}`);
await runMe();
console.log(`\t(good) call end ${index}`);
});
// THIS is the key! Promise.all will wait until all the promises in the
// array have returned!
await Promise.all(promisesForAllTheCalls);
console.log('=== DONE GOOD 1 ===');
}
async function alsoWorksAsIntended() {
console.log('=== START GOOD 2 ===');
const promisesForAllTheCalls = apiCallsToMake.map((runMe) => runMe());
// THIS is the key! Promise.all will wait until all the promises in the
// array have returned!
await Promise.all(promisesForAllTheCalls);
console.log('=== DONE GOOD 2 ===');
}
/**
* TODO: Uncomment ONE AT A TIME to see how things work
*
/*
NOTE: It should print out in this order:
TWO
ONE
ZERO
*/
async function main() {
// ===== Broken on purpose ======
await doesNotWorkAsIntended();
// await stillDoesNotWorkAsIntended();
// ======= These work! =========
// await worksAsIntended();
// await alsoWorksAsIntended();
// this kills the node process to show how badly things can go if
// promises aren't handled properly
process.exit(1); // COMMENT THIS OUT TO SEE THE API RETURNING (broken tests only)
}
main();
/*
Async for of loop Example
*/
// timers in seconds
let x = [5,1,11,3];
// sleep function
async function sleep(seconds, callback) {
if (Number.isNaN(seconds)) throw new TypeError('Seconds must be a number');
return new Promise((resolve) => {
if (callback) {
setTimeout(() => resolve(callback), seconds * 1000);
} else {
setTimeout(resolve, seconds * 1000);
}
});
}
// put in an async function so you can C&P to commandline
async function runDemo() {
for (const item of x) {
console.log('start', item)
await sleep(item, () => console.log('callback for ', item))
console.log('done', item)
}
}
runDemo();
/* expected output should be:
> runDemo();
start 5
Promise { <pending> }
callback for 5
done 5
start 1
callback for 1
done 1
start 11
callback for 11
done 11
start 3
callback for 3
done 3
*/
// START Property Accessors
// This is an example of how you can access properties of an object in several ways
// The cool part is you can use variables to define the _keys_ you want to access
// allowing you to make generic functions and components that can access different variables dynamically.
// Real World example: Create a form that allows you to customize which DB column acts as the label and pass in the whole row object without worrying about preprocessing it
// example data setup
let company = { name: "norima", emails: [{type: 'work', address: 'a@example.com'}, {type: 'personal', address: 'b@example.com'}]};
let anotherCompany = { name: "jamesco", emails: [{type: 'work', address: 'Ja@example.com'}, {type: 'personal', address: 'Jb@example.com'}]};
let companies = { norima: company, jamesco: anotherCompany};
// variables representing a more "real" scenario using customization or loops
let currentCompany = 'norima';
let labelPropertyName = 'type';
// access things
companies[currentCompany].emails[0][labelPropertyName]
companies.norima.emails[0].type;
// They're the same!
companies[currentCompany].emails[0][labelPropertyName] === companies.norima.emails[0].type;
// END Property Accessors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment