Sometimes I get stuck with a problem that I don’t really like: When I can’t execute my little bit of code until another bit of code executes, and there’s no events coming from that other code to tell me that it’s⠀run.
When I find myself in the situation where I need something off of an object, but I can’t get it until the object has been changed by something else, I have a quick and dirty little promise that I use for the⠀task.
Wrap an Interval with a Promise
Basically, what I do here is create an interval that checks to see if some property is not undefined for some arbitrary amount of time. And once it is, I resolve the promise.
Here’s what it looks like:
function getUserDataAsync(tryLimit = 4, waitTime = 300) {
return new Promise((resolve, reject) => {
let attempts = 0;
const intervalId = setInterval(() => {
attempts++;
// check to see if userData now exists on the window
if (window.userData !== undefined) {
// it exists! clear the interval and resolve
clearInterval(intervalId);
resolve(window.userData);
// check to see if we've reached our maximum number of attempts
} else if (attempts === tryLimit) {
// nope, never showed up. Clear out the interval and go ahead and reject
clearInterval(intervalId);
reject(new Error(`userData not found after ${tryLimit} attempts`));
}
}, waitTime);
});
}
And I’d use it something like this:
async function showAuthenticatedContent() {
try {
const userInfo = await getuserDataAsync();
if (userInfo) {
const dashboard = new UserDashboard(userInfo)
dashboard.update();
}
} catch (showAuthenticatedContentError) {
console.error(showAuthenticatedContentError);
}
}
Of course you could make it more generic
Let’s suppose for some reason you have lots of cases where things get added to objects, you need those things, and you don’t know when they’ll be added. Just add a few more parameters:
function getPropertyAsync(object, propertyName, tryLimit = 4, waitTime = 300) {
return new Promise((resolve, reject) => {
let attempts = 0;
const intervalId = setInterval(() => {
attempts++;
if (Object.prototype.hasOwnProperty.call(object, propertyName)) {
clearInterval(intervalId);
resolve(object[propertyName]);
} else if (attempts === tryLimit) {
clearInterval(intervalId);
reject(new Error(`${propertyName} not found after ${tryLimit} attempts`));
}
}, waitTime);
});
}
Obviously this isn’t ideal
It’s just an interval that’s continually checking to see if some object has a property. That’s not great (this BTW, is why you should definitely keep the attempts as low as possible).
The ideal scenario would be that whatever creates userData fired some global event that I could then user addEventListener() to listen for. But sometimes the code that adds these objects is out of my reach; It’s imported from somewhere else and it doesn’t tell my UI that it’s done its thing. So in those cases, this is an approach that can work.
Happy apathetic coding!