How can JavaScript execution pause and wait for a Promise to resolve, mimicking synchronous, blocking behavior?

Greetings fellow JavaScript testers!

I’m currently wrestling with a bit of a challenge in my unit testing setup, and hoping for some insights from the community.

I’m doing unit testing where a test framework loads pages into an iFrame and then runs assertions. I create a Promise that resolves on the iFrame’s onload, then return that Promise. This allows me to call loadUrl(url).then(myFunc) and defer execution until the page is ready.

I use this pattern often to allow DOM changes, like simulating clicks and waiting for UI updates, but it unfortunately leads to many anonymous functions and readability issues. Also, even though QUnit’s assert.async() helps, the test function still seems to finish before the Promise resolves completely.

Is there a clean way in JavaScript to “wait” for a Promise to resolve, ideally synchronously or with something resembling .NET’s WaitHandle.WaitOne(), so that I can get the result in order without breaking the flow of my tests?

Any brilliant ideas or patterns would be hugely appreciated! Thanks! :blush:

Hi @MiroslavRalevic! Your question about cleanly “waiting” for a Promise in JavaScript unit tests without breaking flow really hits a common pain point!

You’re right, JavaScript doesn’t let you truly block execution like .NET’s WaitOne() without freezing the main thread, which we definitely want to avoid. But async/await is about as close as you’ll get to a readable, synchronous-style flow.

If you’re using an async test function, just await your Promise and it’ll pause that block until resolved:

  QUnit.test("iframe test", async assert => {
  const done = assert.async();
  await loadUrl(url); // this will "wait"
  myFunc();
  done();
});

This significantly cleans up those .then() chains and makes your asynchronous test logic much easier to follow. You simply need to mark your test functions as async.

Hope this helps!! Happy coding! :sparkles:

Hello! I totally get the readability pain you’re describing @MiroslavRalevic. I fought with this too when moving from Selenium-style test flow to async/Promise-based stuff in JS.

It feels weird at first, but the trick is truly to go all-in on async/await and stop mixing .then() and assert.async() if you can help it. QUnit now supports async function () for tests, which simplifies things a lot.

Example:

JavaScriptQUnit.test("loads iframe", async assert => {
  await loadIframe("page.html");
  assert.ok(document.querySelector("#something"));
});

The moment I ditched that assert.async() + Promises combo and rewrote everything using await, my tests became way easier to follow and much more readable.

Kudos to @mark-mazay for so good of a reply and thanks to everyone reading this!

Hello @MiroslavRalevic(the curious one) and @mark-mazay and @joe-elmoufak(the helping ones). Let me another perspective to the excellent advice on async/await.

If you’re truly dead set on achieving something closer to blocking behavior (though generally not recommended in the browser’s main thread), you could technically explore abusing Atomics.wait() on a SharedArrayBuffer. However, be warned: it’s really low-level and only works in Web Workers, not the main thread itself.

For your tests, though, I’d strongly reiterate to lean fully into async/await inside your test blocks. You’ll beautifully avoid deep nesting, and it makes your code look significantly closer to synchronous. If readability is still a concern, you could even extract complex logic into named async functions instead of stuffing everything inline.

Well thanks for reading! I hope you find this useful!! :upside_down_face: