Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active May 15, 2024 08:08
Show Gist options
  • Save dfkaye/270836e32a9c95c0ddffc4f0abab566e to your computer and use it in GitHub Desktop.
Save dfkaye/270836e32a9c95c0ddffc4f0abab566e to your computer and use it in GitHub Desktop.
async/await dependency chains and what to do about them
// 13 May 2024
// async/await dependency chains
// 14 May 2024
// added explicit try-catch-finally to test error in "finalizer" function C.
// what
// laughing at ben lesh tweet complaining about async/await being difficult
// without explaining why:
// https://twitter.com/BenLesh/status/1790115611277774913
// why
// async/await dependency chains defeat the purpose of promises to begin with.
// how
// async functions return promises. promises were meant to ease the hell of
// nested event callbacks; however, if a promise is rejected for who knows what
// reason - especially an awaited promise - it must be caught, making any
// dependency between two or more such promises fraught with boilerplate, and
// suddenly we're back in callback hell.
// what ever shall we do?
// build a fake async chain to see how bad or how often the blocking and failing
// occur so we can then design for fallback values and behavior.
// our scenario mostly mimics ben's:
// if C depends on B which depends on A, and both B and A are hidden behind
// fetch requests, we can simulate the dependency of C on B and of B on A by
// faking non-determinism of rejecting or resolving each based on Date.now()
// being even, meanwhile preventing further requests if the fake click target
// is marked "busy".
// apply args to function r.
function Z(r, ...args) {
r.apply({}, args)
}
// return resolve if time is even, else return reject.
function Y(resolve, reject) {
return Date.now() % 2 == 0
? resolve
: reject;
}
// fake fetch or other asynchronous process that returns a promise.
function X(v) {
return new Promise(function(resolve, reject) {
var r = Y(resolve, reject);
var P = async () => Z(r, v);
setTimeout(P, 1000 * Math.random());
});
}
// dependency
function A(v) {
var n = +v == v ? v + 19 : "0";
return X(n).catch(_ => "A")
};
// dependency
function B(v) {
var n = +v == v ? v * 105 : "999";
return X(n).catch(_ => "B")
};
// finalizer
async function C(...args) {
if (Date.now() % 2 == 0) {
throw Error(`C error: [${args}]`);
}
return { value: `[${args}]` };
}
// fake click handler
async function onclick(e) {
var { busy, value } = e;
if (busy) {
console.warn("busy:", e.target, value, `\n\n`);
return
}
e.busy = true;
var a = await A(value);
var b = await B(a);
try {
var c = await C(a, b).catch(v => ({ error: `problem finalizing ${v}` }));
}
catch (err) {
console.assert(!err, "should not see this");
}
finally {
'error' in c
? (console.error("finally", c.error, "for", value, `\n\n`))
: (console.log("success", c.value, "for", value, `\n\n`));
}
e.busy = false;
}
// fake event target
var e = {"target": "test" };
/*** test it out ***/
Array.from({ length: 10 }).forEach((v, i) => {
setTimeout(() => { e.value = i; onclick(e); }, i * 850);
});
/*
busy: test 1
finally problem finalizing Error: C error: [19,1995] for 0
success [21,B] for 2
busy: test 4
finally problem finalizing Error: C error: [22,B] for 3
success [A,B] for 5
busy: test 7
success [25,B] for 6
busy: test 9
success [A,B] for 8
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment