deno_std icon indicating copy to clipboard operation
deno_std copied to clipboard

`assertPromiseResolved()`

Open jespertheend opened this issue 10 months ago • 4 comments

Is your feature request related to a problem? Please describe.

During tests I often want to verify that a promise has been resolved right at this moment.

Describe the solution you'd like

This is a function that I've been using a lot for this:

/**
 * Asserts whether a promise is currently resolved or not. The check is made asynchronously.
 * The call waits for the next event loops and gives the promise a chance to resolve in the current event loop.
 * The reason for this is that there is no way to synchronously check the resolved state of promises in JavaScript.
 * @param {Promise<any>} promise
 * @param {boolean} expected
 */
export async function assertPromiseResolved(promise, expected) {
	let resolved = false;
	(async () => {
		await promise;
		resolved = true;
	})();
	await waitForMicrotasks();
	const msg = expected ? "Expected the promise to be resolved" : "Expected the promise to not be resolved";
	assert(resolved == expected, msg);
}

waitForMicrotasks is a very basic:

export function waitForMicrotasks() {
	return new Promise(r => setTimeout(r, 0));
}

As far as I'm aware this is required, there's no way to figure out the state of promises synchronously.

Is this something that would be suitable for deno_std? I've got a few tests for this as well.

Describe alternatives you've considered

Keep using this from my own repository ¯\_(ツ)_/¯

jespertheend avatar Nov 01 '23 20:11 jespertheend

@iuioiua any thoughts?

lino-levan avatar Nov 04 '23 05:11 lino-levan

~~What's wrong with asserting the value of the resolved promise? Am I missing something here?~~ Nevermind. I see it now.

iuioiua avatar Nov 06 '23 00:11 iuioiua

await waitForMicrotasks() part in the above example looks inaccurate to me (it looks waiting more than microtasks). The suggested util (promiseState) in this stackoverflow answer looks more general (supporting rejected state) and simpler.

kt3k avatar Nov 06 '23 02:11 kt3k

The await waitForMicrotasks() is there because half of the time you need to do that anyway, and when you don't, the extra setTimeout() isn't really an issue. I haven't really run across a case where I needed to know whether a promise was resolved right at this moment.

I figured without this, you'd just be confused as to why your promise isn't resolved. Only to realise after debugging for a good few minutes that you need to wait for microtasks.

Some stats: 28 of my tests use assertPromiseResolve() at least once, 12 of which fail without the waitForMicrotasks() call. I have also tried

export function waitForCurrentMicroTasks() {
	return new Promise(r => queueMicrotask(r));
}

But that still makes 5 of these tests fail.

Being able to check for a rejected state sounds nice though. I usually just use assertRejects() for that since it also allows you to verify the error message.

jespertheend avatar Nov 06 '23 11:11 jespertheend