Is there any reason to use if to check if method exists, before calling await
if (someObject.save){
await someObject.save();
}
rather than using nullish coallescence diectly with await
await someObject.save?.();
Is second code a safe alternative to the former?
Is second code a safe alternative to the former?
Yes. But maybe not for the reason one might expect.
The two pieces of code are equivalent in terms of the check made:
const obj = {
foo() { console.log("method called"); },
bar: 42
};
if (obj.foo) {
console.log("obj has foo");
obj.foo(); //works
}
console.log("using optional chaining");
obj.foo?.(); //works
if (obj.bar) {
console.log("obj has bar");
try {
obj.bar(); //error
} catch (err) {
console.log("bar cannot be called");
}
}
console.log("using optional chaining");
try {
obj.bar?.() //error
} catch (err) {
console.log("bar cannot be called");
}
.as-console-wrapper {
max-height: 100% !important
}
However, in terms of async semantics, there is a difference: await will force an async function to pause. While not having await will run the function until another await is encountered or the function finishes and a result is produced.This can lead to very subtle bugs. Consider this case when the behaviour overlaps - there is a save() method, so both functions pause to await its result:
let x = 1;
async function testIf(someObject) {
console.log("testIf 1:", x); //some external variable
if (someObject.save){
await someObject.save();
}
console.log("testIf 2:", x); //some external variable
}
async function testOptional(someObject) {
console.log("testOptional 1:", x); //some external variable
await someObject.save?.();
console.log("testOptional 2:", x); //some external variable
}
const obj = {
save() { return Promise.resolve(); }
}
testIf(obj);
testOptional(obj);
x = 2; //change external variable
The behaviour is consistent: both of these will stop at the line with await, which will yield the execution, which then processes the new assignment to x, so then when both functions resume, they both read the new value of x. This is expected.
Now, consider what happens if the await line is not hit in the if version:
let x = 1;
async function testIf(someObject) {
console.log("testIf 1:", x); //some external variable
if (someObject.save){
await someObject.save();
}
console.log("testIf 2:", x); //some external variable
}
async function testOptional(someObject) {
console.log("testOptional 1:", x); //some external variable
await someObject.save?.();
console.log("testOptional 2:", x); //some external variable
}
const obj = {}
testIf(obj);
testOptional(obj);
x = 2;
The semantics changed. For the if version the await line is not processed, thus the execution does not yield until the function finishes, thus it reads the value of x before the reassignment both times. The version that uses optional chaining preserves its semantics even if save is not present - it still encounters the await and still yields.
In summary, having optionally asynchronous operation is the path to madness. I would suggest avoiding it at all costs.
Read more about this:
Callbacks, synchronous and asynchronous by Havoc
Designing APIs for Asynchrony by Isaac Z. Schlueter
Intentionally unleashing Zalgo with synchronous promises by Daniel Brain
And yes, the code I showed is also probably bad - you most likely should not rely on external values. Yet, sometimes you have to. And even if you do not rely on them, in the future, somebody consuming your code might. It is best to just avoid Zalgo in the first place and have more predictable code.
The best answer to this (I think) would be if you are working in a group where the group has decided not to use ?. because not everyone understands it. Otherwise, no. The second isn't safer
It should be fine as you would await undefined.
Some considerations from the documentation:
const result = someInterface.customMethod?.();
if there is a property with such a name which is not a function, using ?. will still raise a TypeError exception "someInterface.customMethod is not a function".
Note: If someInterface itself is null or undefined, a TypeError exception will still be raised ("someInterface is null"). If you expect that someInterface itself may be null or undefined, you have to use ?. at this position as well: someInterface?.customMethod?.().
Related
so I am having some issues with scopes when it comes to nodejs. I am wondering how you can initialize a variable in the global space, initialize it in a function (private scope) then have it equal to what you initialized it to in the private scope when you call it anywhere in the class. One thing I have noticed is that although you can use the variable in a private scope, when it is called again outside that function it just turns undefined. How can I return the same functions initialized when it was private? This is not my issued code but an example in case the picture isn't clear
let name;
class blah {
static blablah() {
name = josh;
}
console.log(name);
//this will return undefined and not josh
}
What I need in my context:
let genesis;
let jsonChain;
class Blockchain {
constructor() {
//this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis());
}
//down the file we find the issued function...
static getGenesis() {
fs.readFile(jsonRoute, 'utf-8', function(err, data) {
if (err) throw err;
jsonChain = JSON.parse(data);
genesis = jsonChain.blocks[0].GENESIS_DATA;
return genesis;
});
//returning here instead inside the callback also yields undefined
//I want to be able to access my contents from json file through
//the genesis variable which is global when I return this function
//but I cannot reach the contents of the callback
}
}
SOLUTION: Make sure to return the promise so that the async function runs later in your program when you call it.
let genesis;
let jsonChain;
class Blockchain {
constructor() {
this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis().then(function(genesisData) {
console.log(genesisData); // Genesis block data is here.
}, function(err) {
// This only runs if there was an error.
console.log(err);
}));
}
//down the file we find the solved function...
static getGenesis() {
return new Promise(function(resolve, reject) {
fs.readFile(jsonRoute, 'utf-8', function(err, data) {
if(err) return reject(err);
const jsonChain = JSON.parse(data);
resolve(jsonChain.blocks[0].GENESIS_DATA);
});
});
}
}
module.exports = Blockchain;
Now that you've added the real code, there are a number of issues illustrated there.
if (err) throw err inside a plain asynchronous callback does nothing useful. Nobody can catch that error and thus this should never be used. You need real error handling there. IMO, one should never write that line of code in that circumstance.
return genesis; does nothing useful. That just returns back from the fs.readFile() callback where the internals of fs.readFile() are ignoring any return value. That does not return from the higher level function.
jsonChain = JSON.parse(data); does successfully assign to the higher scoped variable, but any code that is trying to use the jsonChain variable will have no idea when it was assigned a value because fs.readFile() is asynchronous and calls its callback at some future, indeterminate time. So, if you're seeing that jsonChain has no value, that's because you're looking at the variable BEFORE it gets assigned. Basically, this is an anti-pattern. You can't program with asynchronous code that way in node.js. Instead, you need to USE the asynchronous value inside the callback or you need to call some function from within the callback and pass it the value. ONLY in that circumstance do you know WHEN the value is available.
If the top level problem here is that you're trying to return some value you retrieved asynchronously, then you cannot directly do that in Javascript. You can read all about that here where it explains that you have to communicate that asynchronous value back to the caller with a callback, a promise or you can also use an event.
The first problem with your code is that josh is not in quotes. josh is undefined; did you mean 'josh' (in quotes)?
One way is to make your getGenesis() function async, so that you can wait for it to finish assigning the correct value to genesis. This requires wrapping everything inside an anonymous async function:
(async () => {
const fs = require('fs');
let genesis;
let jsonChain;
class Blockchain {
constructor() {
this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis());
}
static async getGenesis() {
jsonChain = JSON.parse(await fs.promises.readFile(jsonRoute, 'utf-8'));
genesis = jsonChain.blocks[0].GENESIS_DATA;
}
}
console.log(genesis); // undefined D:
await Blockchain.getGenesis();
console.log(genesis); // not undefined :D
})();
The following code uses an async iterator to deliver "frames".
But this approach leaves an unused variable (_).
If this is a valid approach, why was while await (a hypothetical feature) not added when for await...of was?
while await would enable the omission of this ignored variable.
const raf = () => new Promise(resolve => requestAnimationFrame(resolve))
const frames = {
async *[Symbol.asyncIterator]() {
while (true) yield raf()
}
}
function createGameLoop(store) {
async function start() {
for await (let _ of frames)
render(store)
}
return { start }
}
why was while await (a hypothetical feature) not added when for await...of was?
Because it adds unnecessary complexity to the language implementation, and is rarely ever useful.
Generators ought to produce values, not nothings. Ignoring the value is easy by not using the variable, as you did. The main use case is getting values, and that's what the syntax was designed for.
They actually would be useful even in your example. Your generator doesn't deliver nothing, it doesn't deliver "frames", it does deliver the high resolution timestamps that requestAnimationFrame calls resolve with, and you should be using them in your render function for smooth delta animation:
function createGameLoop(store) {
async function start() {
for await (const timestamp of frames)
render(store, timestamp)
}
return { start }
}
I want to refactor a promise chain into async/await, but Typescript is complaining about the typing.
TS2322:Type 'IHttpPromiseCallbackArg< IResp >' is not assignable to type 'IResp'...
I thought await would return a regular value, not a promise. Am I wrong? If so, how can I assign a typing so that the desired code will compile?
I thought await would return the same value as the first argument in the .then callback. Am I wrong?
Old code:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
Desired new code:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
The desired code still breaks if I remove the return type in myAsyncRequest ; I guess Typescript infers directly from the AngularJS library.
myAsyncRequest(params:IParams):IHttpPromise<IResp> {
return $http.post('blah', data);
}
If I remove "IResp" from the const resp declaration, processResponse complains that IHttp< IResp> does not equal IResp...
processResp(resp:IResp) {
//do stuff
}
Your question "I thought await would return the same value as the first argument in the .then callback. Am I wrong?".
No, you are absolutely right. But you are wrong about what the first argument in the .then callback is.
You define myAsyncRequest to return IHttpPromise<IResp>. But IHttpPromise<T> is defined as inheriting IPromise the following way:
type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;
So, an IHttpPromise<T> is a promise that returns an IHttpPromiseCallbackArg<T> back where the actual data of type T is in the data property of the IHttpPromiseCallbackArg<T>.
So, the old code variant we see in the question:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
should actually not compile without errors in TypeScript when myAsyncRequest is defined to return IHttpPromise.
Howto fix this:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params);
const resp: IResp = httpResp.data;
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Note: In the latest type definitions for angular, the type IHttpPromiseCallbackArg<T> is actually called IHttpResponse<T>.
Maybe in your code, you have defined IResp as IHttpResponse<Something>? Then you just have a conflict with the old name IHttpPromiseCallbackArg. Then get the newest type definitions from DefinitelyTyped that uses the new name. And you would also have to change the definition of myAsyncRequest to:
myAsyncRequest(params:IParams):IHttpPromise<Something> {
The line containing the await does indeed await the resolved value - but because the function itself is async (to allow all that awaiting), you get back a promise.
Example... in the below code you can use x as a plain number (even though delay returns a promise) and again for y - so all the stuff you await is resolved so you can use it more like you would if it were synchronous.
The async function that doesn't look like it returns a promise, now does.
This can seem confusing, because it seems to "invert the promises", but what it does is shift the then to the top level (you could have async functions calling down to other async functions and so on).
function delay(ms: number) {
return new Promise<number>(function(resolve) {
setTimeout(() => {
resolve(5);
}, ms);
});
}
async function asyncAwait() {
let x = await delay(1000);
console.log(x);
let y = await delay(1000);
console.log(y);
return 'Done';
}
asyncAwait().then((result) => console.log(result));
As a simplified case, I have two async functions, foo and bar. bar needs the result of foo, i.e. bar depends on foo. I have no idea about which function will be called first.
If bar is invoked first, bar will call foo and start itself right after foo is done.
If foo is invoked first and done, bar can use the result of foo.
If foo is invoked first and bar is invoked before foo is done, bar needs to wait for foo's result. (Don't invoke a new call to foo, just wait for the already-fired call to foo)
How can I achieve this?
Is it possible to register an async function dependency chain (something like the dependency in require.js define['foo'], function() { bar(); })?
Can I use $.deferred() to achieve it?
How?
In circumstances like this, the standard approach is to cache the lower level promise.
Typically you will establish, in some suitable outer scope, a js plain object as a promise cache, and always look there first before calling your async process.
var promiseCache = {};
function foo() {
if(!promiseCache.foo) {
promiseCache.foo = doSomethingAsync();
}
return promiseCache.foo;
}
function bar() {
return foo().then(doSomethingElseAsync);
}
Of course, there's nothing to prevent you also caching the higher level promise, if appropriate.
function bar() {
if(!promiseCache.bar) {
promiseCache.bar = foo().then(doSomethingElseAsync);
}
return promiseCache.bar;
}
EDIT: forceRefresh feature
You can force a function to refresh its cached promise by passing an (extra) parameter.
function foo(any, number, of, other, arguments, forceRefresh) {
if(forceRefresh || !promiseCache.foo) {
promiseCache.foo = doSomethingAsync();
}
return promiseCache.foo;
}
By making forceRefresh the last argument, leaving it out is the same as passing false and foo will use the cached promise if available. Alternatively, pass true to guarantee that doSomethingAsync() be called and the cached value be refreshed.
EDIT 2: setName()/getName()
With the forceRefresh mechanism in place in getName() :
setName(newName).then(getName.bind(null, true)); //set new name then read it back using forceRefresh.
Alternatively, omit the forceRefresh mechanism and, assuming the cache property to be promiseCache.name :
setName(newName).then(function() {
promiseCache.name = $.when(newName);//update the cache with a simulated `getName()` promise.
});
The first method is more elegant, the second more efficient.
You can simply think of both functions as independent. That way, you don't go daisy-chaining dependencies that operate asynchronously. You can then have one other module that uses them.
Since they do async stuff, consider using promises. You can use jQuery's deferreds for compatibility. Think of deferreds as read/write while promises are read-only.
// foo.js
define(function(){
return function(){
return new Promise(function(resolve, reject){
// Do async stuff. Call resolve/reject accordingly
});
};
});
// bar.js
define(function(){
return function(){
return new Promise(function(resolve, reject){
// Do async stuff. Call resolve/reject accordingly
});
};
});
// Your code (Excuse the CommonJS format. Personal preference)
define(function(require){
// Require both functions
var foo = require('foo');
var bar = require('bar');
// Use them
foo(...).then(function(response){
return bar();
}).then(function(){
// all done
});;
});
Try creating an object property with possible values undefined , "pending" , true ; call deferred.resolve() when obj.active is true , deferred.reject() when obj.active is "pending"
var res = {
active: void 0
};
var foo = function foo(state) {
var t;
var deferred = function(type) {
return $.Deferred(function(dfd) {
if (res.active === "pending" || state && state === "pending") {
res.active = "pending";
dfd.rejectWith(res, [res.active])
} else {
res.active = state || "pending";
t = setInterval(function() {
console.log(res.active)
}, 100);
setTimeout(function() {
clearInterval(t)
res.active = true;
dfd.resolveWith(res, [res.active])
}, 3000);
}
return dfd.promise()
})
.then(function(state) {
console.log("foo value", state);
return state
}, function(err) {
console.log("foo status", err)
return err
})
}
return deferred()
}
var bar = function bar(result) {
var deferred = function(type) {
return $.Deferred(function(dfd) {
if (result && result === true) {
setTimeout(function() {
dfd.resolveWith(result, [true])
}, 1500)
} else {
dfd.rejectWith(res, [res.active || "pending"])
};
return dfd.promise()
})
}
return deferred().then(function(data) {
console.log("bar value", data);
}, function(err) {
console.log("bar status", err);
})
}
$("button").click(function() {
$(this).is(":first")
? foo().then(bar, bar)
: bar(res.active === true ? res.active : "pending")
.then(foo, foo).then(bar, bar)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>foo</button>
<button>bar</button>
Not sure I understood correctly the question. But here is my take at it:
Put you function foo into a variable
var foo_fn = function foo(foo_args){// Your async code goes here}
foo is async and returns something at some point. In your definition of foo, I recommend that you use promises, the concept is designed to manage composition of asynchronous functions in a clean and scalable way. jQuery implementation of the concept is convenient in a lot of simple use cases but suffers from some drawbacks which make it interesting for you at some point to use one of the many promises library which follow the Promises/A specification. For more information, you can refer to :
Cf. https://thewayofcode.wordpress.com/2013/01/22/javascript-promises-and-why-jquery-implementation-is-broken/ and https://blog.domenic.me/youre-missing-the-point-of-promises
so, say foo takes args, and returns a promise which later resolves into some value.
var foo_fn = function foo(foo_args) {
return foo_fn.promise = new RSVP.Promise (resolve, reject) {
// Your async code goes here
}
}
Here I use the RSVP promise library but any promise library following the Promises/A specification could do the job.
When bar is called, you can just do:
function bar (bar_args) {
var foo_promise = foo_fn.promise;
// if foo was called, whether the computation is in progress or finished,
// the foo_fn.promise field will be non-empty, as foo returns immediately
// with a promise anytime it is called
``
if (!foo.promise) {
// foo has not yet been called so call it
foo_promise = foo(foo_args);
}
foo_promise.then (function (foo_result) {/*some async code here*/})
}
NOTE : That solution is quite similar to the one proposed by Roamer-1888. One difference is that in Roamer proposal, the foo function will always return the same value after performing once its asyncronous computation. Don't know if this is the intended behaviour. In my implementation, foo executes the async. computation every time it is called. bar will use the latest computed value that is stored in the field foo_fn.promise. Older computations are lost, possible computation in progress is not taken into account.
If you are going to have this pattern often used in your code, you can also create a function working on the model of the define
function in require.js.
You will need :
a registry to hold the dependencies functions (foo in your example)
the dependant function (bar in your example) will need to accept the dependencies functions computed value as part of their signature. For example, a hash of the dependencies could be passed as first parameter, so bar signature could be: {foo: foo_result}, other_bar_args...
the dependencies function must follow the model of my previous answer, i.e. register their promise value as a property on themselves when they execute.
Reminder : you need to name those dependencies functions to reference them inside their body, and then add that object to the registry.
In the define function body, you wrap the dependent function into another one which :
Get all dependencies from the registry
Get all dependencies values, executing the dependencies when necessary (similarly to my previous answer). This means you end up having a list of promises, whose results you then congregate together (RSVP.hash for example with RSVP promise library). I believe jQuery has a similar function with jQuery.when
you call the dependent function (bar) with this hash of results as a first argument, other arguments being the same as the wrapped function
that wrapped function is the new bar, so when bar is called, it will be the wrapped function which will be called.
A bit lengthy but it should work. If you want to see some code, let me know if this is what you were looking for. In any case, if you are going to have complex async. in your code, it could be interesting for you to use a compliant promise library. $.deferred is also to be used only when you have nothing better at sight as it makes it harder for you to track the behaviour of your functions : you need to keep track of all places where this deferred appears to be able to reason about your program.
Let's say I have a function that looks like this:
var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
});
asyncFuncTwo(function(y) {
final.y = y;
});
callback(final);
});
Obviously, this doesn't do what I want it to do (call callback on final when it has both x and y). I have several questions:
Is there a way to do what I want it to do without nesting everything?
Does the current form introduce a race condition? Are both async functions accessing the same final?
Approach #0. Painful life without promises. Yet life
Actually, your code like cries to be rewritten in promises. Trust me, this refactoring is something you 100% need. But ok, let's try to solve this particular problem without invoking promises at all - just as an exercise. Actually before the promise era the pattern was to introduce a special function that checks whether we can consider that we are done or not.
In your particular case such function is:
function weAreDone() {
return final.hasOwnPropery('x') && final.hasOwnProperty('y')
}
Then we can introduce asyncFuncDecorator:
function asyncFuncDecorator = function(asyncFunc, asyncFuncHandler) {
return function(doneFunc, doneHandler) {
asyncFunc(asyncFuncHandler);
if (doneFunc()) {
doneHandler();
}
}
}
With this two functions introduced you can write something like:
var foo = function(callback) {
var final = {};
//here goes abovementioned declarations
...
asyncFuncDecorator(asyncFuncOne, function(x) {
final.x = x;
})(weAreDone, callback);
asyncFuncDecorator(asyncFuncTwo, function(y) {
final.y = y;
})(weAreDone, callback);
});
You can keep working on making this approach more flexible and universal but, once again, trust me,
you'll end up with something very similar to promises, so better promises ;)
Approach #1. Promisifying existing functions
If, for some reason, you are not ready to rewrite all you functions from callback style to promises,
you can promisify existing functions by using, once again, a decorator. Here's how it can be done for native Promises, which are present in all modern browsers already (for alternatives, check this question):
function promisify(asyncCall){
return new Promise(function(resolve,reject){
asyncCall(resolve,reject);
});
}
In that case you can rewrite you code in this fashion:
var foo = function(callback) {
//here goes abovementioned declarations
...
Promise.all([promisify(asyncFuncOne), promisify(asyncFuncTwo)]).then(function(data) {
// by the way, I'd rather not to call any variable "final" ))
final.x = data[0];
final.y = data[1];
}).then(callback);
});
Not to say that actually foo it's better to be promisified itself ;)
Approach #2. Promises everywhere. From the very beginning
It worth to reiterate this thought - as soon as you need to trigger some function after N other async functions should be completed - promises in 99% cases are unbeatable. It almost always worth trying to rewrite existing code to in promise-based style. Here's how can such code look like
Promise.all([asyncFuncOne(), asyncFuncTwo()]).then(function(data) {
return Promise.resolve({
x: data[0],
y: data[1]
})
}).then(callback);
See how much better it become. Also, a common mistake of using promises - is to have a sequential waterfall of thens - retrieving first chunk of data, only after that - the second one, after that - the third one. You actually never should do this unless you are transforming data received in Nth request depending on what you've got in one of your previous requests - instead just use all method.
This is very crucial to understand. This is one of main reasons why promises quite often are misunderstood as something excessively complicated.
Sidenote: as of December'14, native Promises are natively supported by all major modern browsers except IE, and in Node.js has native promise support is a thing since version 0.11.13, so in real-life you still most probably will need to use promise library. There's a lot of Promise spec implementations, you can check this page for the list of standalone promise libraries, it's quite big, the most popular solutiona are, I guess, Q and bluebird.
Approach #3. Generators. Our bright future. Well, may be
This is something worth to mention, generators are de-facto supported in Firefox, Chromium-based browsers and node.js (called with --harmony_generators option). So, de-facto, there are cases when generators can be used, and actually are already used, in production code. It's just that if you are writing a general-purpose web app, you should be aware of this approach but you'll probably won't use it for a while. So, you can use the fact that generators in js allow you to invoke two-way communication through yield/iterator.next(). In that case.
function async(gen) {
var it = gen();
var state = it.next();
var next = function() {
if (state.done) {
return state.value;
};
state.value(function(res) {
state = it.next(res);
next();
});
}
next();
}
async(function* () {
var res = {
x: yield asyncFuncOne,
y: yield asyncFuncTwo
}
callback(res);
});
Actually, there are already dozens of libraries which do this generator wrapping job for you.
You can read more about this approach and related libraries here.
Another solution is to create a setter:
var foo = function (callback) {
var final = {
setter: function(attr,value){
this[attr] = value;
if (this.hasOwnProperty("x") && this.hasOwnProperty("y"))
callback(this);
}
};
asyncFuncOne(function(x) {
final.setter("x", x);
});
asyncFuncTwo(function(y) {
final.setter("y", y);
});
};
final.x and final.y are set on final, but after it's sent to callback so, unless the callback is waiting, x and y are undefined when callback receives them.
You could check to see if one has come back in the response of the others and call out to the callback:
var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
if (typeof final.y !== 'undefined') {
callback(final);
}
});
asyncFuncTwo(function(y) {
final.y = y;
if (typeof final.x !== 'undefined') {
callback(final);
}
});
});
You could nest your callbacks, though this will cause asyncfuncTwo to not be called until asyncfuncOne has finished):
var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
asyncFuncTwo(function(y) {
final.y = y;
callback(final);
});
});
});
Then there are Promises. These are the future of async however they are not fully supported across all browsers (namely, all of IE [11 and below at the this time]). In fact, 40% of all browser users are not using a browser that natively supports Promises. This means you will have to use a polyfill library to give you support adding substantial filesize to your page. For this simple problem and at this given time I wouldn't recommend using Promises for this simple issue. However, you should definitely read up on how they are used.
If you want to see what that could look like, it'd be this:
var asyncFuncOne = function() {
return new Promise(function(resolve, reject) {
// A 500 seconds async op and resolve x as 5
setTimeout(function() { resolve(5); }, 500);
});
};
var asyncFuncTwo = function() {
return new Promise(function(resolve, reject) {
// A 750ms async op and resolve y as 10
setTimeout(function() { resolve(10); }, 750);
});
};
var foo = function() {
var final = {};
return new Promise(function(resolve, reject) {
Promise.all([
asyncFuncOne(),
asyncFuncTwo()
]).then(function(values) {
final.x = values[0];
final.y = values[1];
resolve(final);
});
});
};
foo().then(function(final) {
// After foo()'s Promise has resolved (750ms)
console.log(final.x + ', ' + final.y);
});
Note no callbacks, just use of then. In a real scenario you would also use catch and reject. Read more about Promises here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise but, again, I personally don't see a strong need to use them for this single, specific issue (but, to each their own).
One pretty bad idea, but I've had to use it before, because I wasn't about to import a 50k promise library for a single function, would be to set a looping Timeout that checks to see if all the required variables are set, and then calls the callback.