I'm doing some unit testing. The test framework loads a page into an iFrame and then runs assertions against that page. Before each test begins, I create a Promise
which sets the iFrame's onload
event to call resolve()
, sets the iFrame's src
, and returns the promise.
So, I can just call loadUrl(url).then(myFunc)
, and it will wait for the page to load before executing whatever myFunc
is.
I use this sort of pattern all over the place in my tests (not just for loading URLs), primarily in order to allow changes to the DOM to happen (e.g. mimick clicking a button, and wait for divs to hide and show).
The downside to this design is that I'm constantly writing anonymous functions with a few lines of code in them. Further, while I have a work-around (QUnit's assert.async()
), the test function that defines the promises completes before the promise is run.
I'm wondering if there is any way to get a value from a Promise
or wait (block/sleep) until it has resolved, similar to .NET's IAsyncResult.WaitHandle.WaitOne()
. I know JavaScript is single-threaded, but I'm hoping that doesn't mean that a function can't yield.
In essence, is there a way to get the following to spit out results in the correct order?
function kickOff() {_x000D_
return new Promise(function(resolve, reject) {_x000D_
$("#output").append("start");_x000D_
_x000D_
setTimeout(function() {_x000D_
resolve();_x000D_
}, 1000);_x000D_
}).then(function() {_x000D_
$("#output").append(" middle");_x000D_
return " end";_x000D_
});_x000D_
};_x000D_
_x000D_
function getResultFrom(promise) {_x000D_
// todo_x000D_
return " end";_x000D_
}_x000D_
_x000D_
var promise = kickOff();_x000D_
var result = getResultFrom(promise);_x000D_
$("#output").append(result);
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<div id="output"></div>
_x000D_
This question is related to
javascript
asynchronous
promise
async-await
ecma
You can do it manually. (I know, that that isn't great solution, but..)
use while
loop till the result
hasn't a value
kickOff().then(function(result) {
while(true){
if (result === undefined) continue;
else {
$("#output").append(result);
return;
}
}
});
If using ES2016 you can use async
and await
and do something like:
(async () => {
const data = await fetch(url)
myFunc(data)
}())
If using ES2015 you can use Generators. If you don't like the syntax you can abstract it away using an async
utility function as explained here.
If using ES5 you'll probably want a library like Bluebird to give you more control.
Finally, if your runtime supports ES2015 already execution order may be preserved with parallelism using Fetch Injection.
Another option is to use Promise.all to wait for an array of promises to resolve and then act on those.
Code below shows how to wait for all the promises to resolve and then deal with the results once they are all ready (as that seemed to be the objective of the question); Also for illustrative purposes, it shows output during execution (end finishes before middle).
function append_output(suffix, value) {
$("#output_"+suffix).append(value)
}
function kickOff() {
let start = new Promise((resolve, reject) => {
append_output("now", "start")
resolve("start")
})
let middle = new Promise((resolve, reject) => {
setTimeout(() => {
append_output("now", " middle")
resolve(" middle")
}, 1000)
})
let end = new Promise((resolve, reject) => {
append_output("now", " end")
resolve(" end")
})
Promise.all([start, middle, end]).then(results => {
results.forEach(
result => append_output("later", result))
})
}
kickOff()
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Updated during execution: <div id="output_now"></div>
Updated after all have completed: <div id="output_later"></div>
_x000D_
Source: Stackoverflow.com