Consider the following:
function listFoo() {
return $.ajax(...);
}
function listFooey() {
return $.ajax(...);
}
function parallelLoad() {
//Note that we do *not* return the promise here.
$.when(
listFoo(), listFooey()
)
.then (function(fooResponse, fooeyResponse) {
console.log('then')
//doStuff
});
}
//Kick everything off and log
console.log('before parallelLoad');
parallelLoad();
console.log('after parallelLoad');
A typical output on the console would be (jsfiddle) :
before parallelLoad
after parallelLoad
then
The first two functions return the ajax Promise, so the reference to it is not lost. However, the parallelLoad() function does not return the promise, so when it is executed, it creates the promise, returns immediately, and the promise would appear to immediately go out of scope.
In practice, I have not seen a problem arise from this pattern, but I'm wondering if that's just "luck".
In the example above, is parallelLoad() "safe" in that (assuming the ajax calls run without server/network errors) the then clause will always execute?
Or is there a race condition where sometimes the ajax calls might not even be initiated or the promises might be garbage collected before the then clause can run?
(Currently I am using jQuery 3.3.1)
In general, promises that are not going to be resolved and are not longer needed can be garbage-collected before they are resolved.
This is not the case in your example however. Even while there is no obvious global variable holding onto the promise, it still is referenced from the running ajax requests. The requests created by listFoo() and listFooey() keep the callbacks installed on them alive, as they need to run them when they finish. In turn, these callbacks reference the promises (or at least, the deferred parts of them) and the callbacks installed on them, as they need to resolve the promises and call the callbacks when they run. In turn, these callbacks reference the promise (or deferred) created by the $.when call and the callback installed on it, as they still are needed until everything is settled. Only once both requests are done and all callbacks are executed, everything will be garbage-collected.
No, it can never happen that a promise will miss to invoke your callbacks because something got garbage-collected at the wrong time.
There's no problem there, the then will always execute.
One main obvious downside with this is since the parallelLoad promise is lost, there is no continuation and you cannot chain any other events to the success or failure of listFoo, listFooey.
I think the promise chaining style may be the cause of the confusion.
Look at it this way, if you rewrite parallelLoad like so (breaking the chain),
function parallelLoad() {
//Note that we do *not* return the promise here.
var whenPromise = $.when(listFoo(), listFooey());
whenPromise.then(function(fooResponse, fooeyResponse) {
console.log('then');
//doStuff
});
}
You can now probably easily see that the then part is simply just another line of code(with a callback) waiting to be called and so will not be garbage collected until it is done.
After the then gets called, parallelLoad will go out of scope for you, but the references to the promises and callbacks created therein are still held internally in memory until the promises are resolved/rejected and callbacks are called.
Nathan, Closures are about identifiers right? So parallelLoad() references both listFoo and listFooeyidentifiers (as functions) creating two closures. Those closures will remain in place until (at least) each of those functions complete and resolve. That means that parallelLoad() is the Execution Context and has not completed yet. The next step in the process is the .then() in response to the resolve.
That means the Execution Context (parallelLoad()) is established at the parallelLoad() call and does not change until all the code in the .then() has completed, it is a safe paradigm.
Related
I couldn't understand the execution order of the two codes below.
async function main(test)
{
while(1) console.log(test);
}
main("a");
main("b");
This code above logs infinite "a".
async function main(test)
{
while(1) await console.log(test);
}
main("a");
main("b");
While this code above logs infinite "a" and "b".
I wanted to understand async/await better but this behaviour above made me confused. Like how are those log functions handled by event loop?
An async function runs synchronously until the first await, return, or implicit return (the code falling off the end of the function). That's so it can start the asynchronous process that it will later report the completion of (by settling the promise it returns). So your first example logs a forever because that's done by the synchronous part of the first call to main; execution never reaches the second call to main.
Your second example introduces asynchronousness by adding await (it doesn't matter that what await is awaiting isn't a promise [console.log returns undefined]; it gets wrapped in one). That means that the first call to main runs synchronously until after the first console.log, then stops and returns a promise while it waits for the promise wrapped around undefined to settle.¹ That allows the second call to main to go forward, doing the same thing. At that point, the code making the calls to main is complete and any pending promise callbacks can be executed. The first pending promise callback is for the await in the first call to main, so that call does another loop iteration; the second pending promise is for the await in the second call to main, so it does another loop iteration; and so on, and so on, and so on.
You mentioned "concurrency." The two calls to main in your second example do run concurrently but are not multi-threaded; instead, they each alternately get access to the one main thread briefly until their next await. JavaScript works on a model of one thread per "realm" (loosely, per global environment). So you don't have to worry about threading issues like reordered operations or stale caches like you would in a multi-threaded environment (unless you're using shared memory, but you aren't in that code); there's only one thread.
¹ Technically, the promise is already settled in this case before await starts awaiting it (because it's just a promise wrapped around undefined), but when you attach to a promise, it never triggers your code synchronously, even if the promise is already settled.
Using Asynchronous functions within Javascript usually resolves data that is not readily available. Examples include calling from a database and scraping web pages for data.
The code provided endlessly loops because there is no end to the said loop (while). In the context of the code provided, while(1) will always remain true, as there is no condition to state otherwise. In practical use, loops are designed to cease when a condition is met.
Here's a basic example of using a while loop sourced from MDN Docs covering break
let n = 0;
while (n < 3) {
n++;
}
console.log(n);
// expected output: 3
To better understand asynchronous programming within Javascript, I highly recommend the MDN Docs on async functions.
To answer your question regarding console logging's interactions with await: JS will 'freeze' the script when it reads an await and work on completing its defined actions, then returns to read the following line. In the context of the code, Javascript is doing its job. It circles back to a lack of a break or condition for the loop.
I have a function that does a bunch of work then returns a resolved Promise. Is there a difference in the timing when each will be resolved?
Option 1
function returnPromise() {
// do a bunch of stuff
return Promise.resolve();
}
and this way:
Option 2
function returnPromise() {
return new Promise((resolve, reject) => {
//do a bunch of stuff
resolve();
});
}
Does Option 1 release control after all the work is done, and Option 2 release control as soon as the function is called? I feel like this has root in the fact I don't fully understand the event loop. Thanks in advance for helping me understand this.
Is there a difference in the timing when each will be resolved?
No difference. The Promise executor (the callback you pass to new Promise()) is called immediately and synchronously. So, in BOTH cases, you are immediately and synchronously returning an already resolved promise and all the code in your function has already executed.
There should be no meaningful difference in execution timing. Of course, one might take slightly different number of CPU cycles to execute, but both have the exact same outcome and both immediately return a promise that is already resolved so there should be no difference to the calling code at all.
Promise.resolve() is just meant as a more compact (and probably more efficient) means of creating an already resolved promise.
Does Option 1 release control after all the work is done, and Option 2 release control as soon as the function is called?
They both return when all your work is done and they return an already resolved promise. This is because the executor callback to new Promise() is called synchronously (before your function returns).
I feel like this has root in the fact I don't fully understand the event loop. Thanks in advance for helping me understand this.
In this particular circumstance, all your code is run synchronously so there is no particular involvement of the event loop. The returnPromise() function returns (in both cases) when your code in the function is done executing synchronously. There is no async code here.
The event loop gets involved when you do .then() on the returned promise because even though the promise (in both cases) is already resolved, the .then() handler gets queued in the event queue and does not run until the rest of your Javascript after your .then() handler is done executing.
console.log("1");
returnPromise().then(function() {
console.log("2");
});
console.log("3");
This will generate identical output results:
1
3
2
with both versions of your returnPromise() function because .then() handlers are queued until the next tick of the event loop (e.g. after the rest of your current Javascript thread of execution is done).
Is there a difference with doing it this way?
No, both your examples do the exact same thing.
Promise.resolve and Promise.reject are simply static methods on the Promise Class that help you avoid constructing a whole Promise when you don't really need to.
According to MDN:
The executor function is executed immediately by the Promise implementation, passing resolve and reject functions
What were the actual reasons for this decision?
Why promises are not lazy?
What were the actual reasons for this decision?
The revealing Promise constructor with the callback was just an improvement over the older deferred pattern. The callback was never meant to provide choice over the evaluation time, it is supposed to provide a scope with error handling for the resolver functions.
Why promises are not lazy?
Because promises represent the asynchronous result values, nothing more. They're kept simple, without featuring lazyness (and representing the whole computation, with methods to start/repeat/etc). You can trivially get that by using a function that returns a promise instead.
Promises are meant to provide continuations, and actual state of standard Promise object proposes the following pattern:
function doStuff() {
return new Promise((resolve, reject) => { // execution function
// Do stuff here
});
}
I would ask a question myself to understand why Promise's execution function is called immediatelly: where you would do the promised stuff?
Clearly, it's called immediatelly because you're making a promise that some stuff will be successfully or unsucessfully done, and that stuff should start to be processed as soon as you call the enclosing function to avoid confusion to the caller.
The point of a promise is to have something to return from a function that callers can attach their callbacks to rather than pass them in. The constructor is a red herring to answer "why", as it exists solely to wrap old callback-style code in an imperfect world.
All JS functions are synchronous, even those returning a promise (es8's async is syntactic sugar).
The Promise constructor executor function exists to provide unified error handling. Consider:
function foo() {
""(); // throws TypeError
return Promise(resolve => {
""(); // rejects promise with TypeError
});
}
Callers would need both try{ foo().catch(failed); } catch(e) { failed(e); } = Sucks.
So put all your synchronous code inside the executor function to unify errors for your callers. That's what it's for, which I think is your real question.
"Lazy" execution would defeat the purpose of unifying error handling of all your code.
In the OpenUI5 code-base I came across this snippet:
// Wait until everything is rendered (parent height!) before reading/updating sizes.
// Use a promise to make sure
// to be executed before timeouts may be executed.
Promise.resolve().then(this._updateTableSizes.bind(this, true));
It looks like the native Promise function is being used, with no argument being passed to it's resolve function which takes an:
Argument to be resolved by this Promise. Can also be a Promise or a
thenable to resolve.
So, since it looks like the promise would simply immediately resolve and invoke then's callback, perhaps the intent is similar to:
var self = this;
setTimeout(function() {
self._updateTableSizes.bind(self, true)
}, 0);
...basically, freeing the JavaScript run-time event-loop to finish other things (like rendering) and then come right back to the callback?
My question is:
Is this a common pattern? Best-practice? Are there any advantages/disadvantages to either approach?
Yes, Promise.resolve() does immediately fulfill with the undefined value that you implicitly passed in. The callback is still executed asynchronously - quite like in the setTimeout snippet you posted.
However, as the comment in the code explains, the intent is not just to execute the callback asynchronously:
Use a promise to make sure to be executed before timeouts may be executed.
Promise callbacks do run before timeouts or other events, and these subtle timing differences are sometimes important. Given that choice of the task loop is usually not important, No this is not a common pattern; but it is a valid pattern that does exactly what you need when you need it.
I noticed the technique in this polyfill: https://github.com/wicg/inert (with comment)
const newButton = document.createElement('button');
const inertContainer = document.querySelector('[inert]');
inertContainer.appendChild(newButton);
// Wait for the next microtask to allow mutation observers to react to the DOM change
Promise.resolve().then(() => {
expect(isUnfocusable(newButton)).to.equal(true);
});
What is the difference between using the Nodejs Q promise library's .finally() and .done() statements.
For example whats the difference between these two?
Q(...)
.then(...)
.finally(); //or fin()
Q(..)
.then()
.done();
promise.done(onSuccess, onError) simply allows you to process resolved value. An additional benefit is that does not imply any error swallowing (as it is the case with promise.then()), it guarantees that any involved exception would be exposed. It also effectively ends the chain and does not return any further promise.
promise.finally(fn) is for registering a task that must be done after a given promise resolves (it doesn't matter whether promise succeeds or fails). Usually, you use it for some kind of cleanup operations e.g. imagine you set up a progress bar, that needs to be hidden after the request is done (no matter if it was successful), then just do promise.finally(hideProgressBar). Additionally promise.finally() returns input promise, so you can return it for further processing.
The difference is in chaining and error handling, and error logging:
Q(...)
.then(...)
.finally();
Here, if the then throws, the finally will still run, but no error will log. In Q finally is run regardless of the .then being successful or not. This is like the finally keyword in JS try/catch/finally. It is also possible to chain additional thens to the chain in this case.
Q(..)
.then()
.done();
Here, done indicates that the promise chain has ended, you can not chain to it any more. If you pass it only an onFulfilled handler it will not run if the then threw, and it will log errors if it ends with an exception.