Created
December 18, 2018 10:58
-
-
Save FauxFaux/8880b9bf4015eb6dad9fbe37a52b0a21 to your computer and use it in GitHub Desktop.
Type system can't save you from missing awaits
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
% npm install typescript | |
.. warnings blah blah.. | |
+ [email protected] | |
updated 1 package and audited 2 packages in 0.538s | |
found 0 vulnerabilities | |
% node_modules/.bin/tsc --lib es2018,dom --target es2018 --strict --noImplicitReturns --noImplicitAny --out hello.js hello.ts | |
% node hello.js | |
buggy1: .- startup | |
buggy1: | β lock | |
buggy1: | β unlock | |
buggy1: | π‘ inside | |
buggy1: '- done | |
buggy2: .- startup | |
buggy2: | β lock | |
buggy2: | β unlock | |
buggy2: | π‘ inside | |
buggy2: '- done | |
buggy3: .- startup | |
buggy3: | π‘ inside | |
buggy3: | β lock | |
buggy3: | β unlock | |
buggy3: '- done | |
buggy4: .- startup | |
buggy4: | β lock | |
buggy4: | π‘ inside | |
buggy4: '- done | |
buggy4: | β unlock | |
buggy5: .- startup | |
buggy5: | π‘ inside | |
buggy5: '- done | |
buggy5: | β lock | |
buggy5: | β unlock | |
buggy6: .- startup | |
buggy6: | β unlock | |
buggy6: | π‘ inside | |
buggy6: '- done | |
buggy6: | β lock | |
okay1: .- startup | |
okay1: | β lock | |
okay1: | π‘ inside | |
okay1: | β unlock | |
okay1: '- done | |
okay2: .- startup | |
okay2: | β lock | |
okay2: | π‘ inside | |
okay2: | β unlock | |
okay2: '- done | |
app complete | |
*/ | |
class Lock { | |
name: string; | |
constructor(name: string) { | |
this.name = name; | |
} | |
async lock(): Promise<void> { | |
await delay(30); | |
console.log(`${this.name}: | β lock`); | |
} | |
async unlock(): Promise<void> { | |
await delay(10); | |
console.log(`${this.name}: | β unlock`); | |
} | |
} | |
async function buggy1<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
await l.lock(); | |
try { | |
// no await, resolved in parent | |
return cb(); | |
} finally { | |
await l.unlock(); | |
} | |
} | |
async function buggy2<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
await l.lock(); | |
try { | |
// still no await, explicit resolve does nothing | |
return Promise.resolve(cb()); | |
} finally { | |
await l.unlock(); | |
} | |
} | |
async function buggy3<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
// no waiting for lock | |
l.lock(); | |
try { | |
return await cb(); | |
} finally { | |
await l.unlock(); | |
} | |
} | |
async function buggy4<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
await l.lock(); | |
try { | |
return await cb(); | |
} finally { | |
// no waiting for unlock | |
l.unlock(); | |
} | |
} | |
async function buggy5<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
// no waiting for either | |
l.lock(); | |
try { | |
return await cb(); | |
} finally { | |
l.unlock(); | |
} | |
} | |
async function buggy6<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
// no waiting for anything | |
l.lock(); | |
try { | |
return cb(); | |
} finally { | |
l.unlock(); | |
} | |
} | |
// wait for everything | |
async function okay1<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
await l.lock(); | |
try { | |
return await cb(); | |
} finally { | |
await l.unlock(); | |
} | |
} | |
async function okay2<T>(l: Lock, cb: () => Promise<T>): Promise<T> { | |
await l.lock(); | |
try { | |
// wait for everything, with an explicit promise | |
return Promise.resolve(await cb()); | |
} finally { | |
await l.unlock(); | |
} | |
} | |
// driver | |
(async () => { | |
for (const func of [buggy1, buggy2, buggy3, buggy4, buggy5, buggy6, okay1, okay2]) { | |
const name = func.name; | |
console.log(`${name}: .- startup`); | |
await func(new Lock(name), async () => { | |
await delay(20); | |
console.log(`${name}: | π‘ inside`) | |
}); | |
console.log(`${name}: '- done`); | |
await delay(70); | |
} | |
})().then(() => console.log('app complete')); | |
function delay<T>(millis: number, value?: T): Promise<T> { | |
return new Promise((resolve) => setTimeout(() => resolve(value), millis)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment