Access and modify website variables using Javascript [duplicate] - javascript

I have a click event that is triggered from another place automatically for the first time. My problem is that it runs too soon, since the required variables are still being defined by Flash and web services. So right now I have:
(function ($) {
$(window).load(function(){
setTimeout(function(){
$('a.play').trigger("click");
}, 5000);
});
})(jQuery);
The problem is that 5 seconds for a person with a slow internet connection could be too fast and vice versa, for a person with a fast internet connection, it's too slow.
So how should I do the delay or timeout until someVariable is defined?

The following will keep looking for someVariable until it is found. It checks every 0.25 seconds.
function waitForElement(){
if(typeof someVariable !== "undefined"){
//variable exists, do what you want
}
else{
setTimeout(waitForElement, 250);
}
}

async, await implementation, improvement over #Toprak's answer
(async() => {
console.log("waiting for variable");
while(!window.hasOwnProperty("myVar")) // define the condition as you like
await new Promise(resolve => setTimeout(resolve, 1000));
console.log("variable is defined");
})();
console.log("above code doesn't block main function stack");
After revisiting the OP's question. There is actually a better way to implement what was intended: "variable set callback". Although the below code only works if the desired variable is encapsulated by an object (or window) instead of declared by let or var (I left the first answer because I was just doing improvement over existing answers without actually reading the original question):
let obj = encapsulatedObject || window;
Object.defineProperty(obj, "myVar", {
configurable: true,
set(v){
Object.defineProperty(obj, "myVar", {
configurable: true, enumerable: true, writable: true, value: v });
console.log("window.myVar is defined");
}
});
see Object.defineProperty
or use es6 proxy (which is probably overkill)
If you are looking for more:
/**
* combining the two as suggested by #Emmanuel Mahuni,
* and showing an alternative to handle defineProperty setter and getter
*/
let obj = {} || window;
(async() => {
let _foo = await new Promise(res => {
Object.defineProperty(obj, "foo", { set: res });
});
console.log("obj.foo is defined with value:", _foo);
})();
/*
IMPORTANT: note that obj.foo is still undefined
the reason is out of scope of this question/answer
take a research of Object.defineProperty to see more
*/
// TEST CODE
console.log("test start");
setTimeout(async () => {
console.log("about to assign obj.foo");
obj.foo = "Hello World!";
// try uncomment the following line and compare the output
// await new Promise(res => setTimeout(res));
console.log("finished assigning obj.foo");
console.log("value of obj.foo:", obj.foo); // undefined
// console: obj.foo is defined with value: Hello World!
}, 2000);

I would prefer this code:
function checkVariable() {
if (variableLoaded == true) {
// Here is your next action
}
}
setTimeout(checkVariable, 1000);

I prefer something simple like this:
function waitFor(variable, callback) {
var interval = setInterval(function() {
if (window[variable]) {
clearInterval(interval);
callback();
}
}, 200);
}
And then to use it with your example variable of someVariable:
waitFor('someVariable', function() {
// do something here now that someVariable is defined
});
Note that there are various tweaks you can do. In the above setInterval call, I've passed 200 as how often the interval function should run. There is also an inherent delay of that amount of time (~200ms) before the variable is checked for -- in some cases, it's nice to check for it right away so there is no delay.

With Ecma Script 2017 You can use async-await and while together to do that
And while will not crash or lock the program even variable never be true
//First define some delay function which is called from async function
function __delay__(timer) {
return new Promise(resolve => {
timer = timer || 2000;
setTimeout(function () {
resolve();
}, timer);
});
};
//Then Declare Some Variable Global or In Scope
//Depends on you
let Variable = false;
//And define what ever you want with async fuction
async function some() {
while (!Variable)
await __delay__(1000);
//...code here because when Variable = true this function will
};
////////////////////////////////////////////////////////////
//In Your Case
//1.Define Global Variable For Check Statement
//2.Convert function to async like below
var isContinue = false;
setTimeout(async function () {
//STOPT THE FUNCTION UNTIL CONDITION IS CORRECT
while (!isContinue)
await __delay__(1000);
//WHEN CONDITION IS CORRECT THEN TRIGGER WILL CLICKED
$('a.play').trigger("click");
}, 1);
/////////////////////////////////////////////////////////////
Also you don't have to use setTimeout in this case just make ready function asynchronous...

You can use this:
var refreshIntervalId = null;
refreshIntervalId = setInterval(checkIfVariableIsSet, 1000);
var checkIfVariableIsSet = function()
{
if(typeof someVariable !== 'undefined'){
$('a.play').trigger("click");
clearInterval(refreshIntervalId);
}
};

Here's an example where all the logic for waiting until the variable is set gets deferred to a function which then invokes a callback that does everything else the program needs to do - if you need to load variables before doing anything else, this feels like a neat-ish way to do it, so you're separating the variable loading from everything else, while still ensuring 'everything else' is essentially a callback.
var loadUser = function(everythingElse){
var interval = setInterval(function(){
if(typeof CurrentUser.name !== 'undefined'){
$scope.username = CurrentUser.name;
clearInterval(interval);
everythingElse();
}
},1);
};
loadUser(function(){
//everything else
});

Instead of using the windows load event use the ready event on the document.
$(document).ready(function(){[...]});
This should fire when everything in the DOM is ready to go, including media content fully loaded.

Shorter way:
var queue = function (args){
typeof variableToCheck !== "undefined"? doSomething(args) : setTimeout(function () {queue(args)}, 2000);
};
You can also pass arguments

I have upvoted #dnuttle's answer, but ended up using the following strategy:
// On doc ready for modern browsers
document.addEventListener('DOMContentLoaded', (e) => {
// Scope all logic related to what you want to achieve by using a function
const waitForMyFunction = () => {
// Use a timeout id to identify your process and purge it when it's no longer needed
let timeoutID;
// Check if your function is defined, in this case by checking its type
if (typeof myFunction === 'function') {
// We no longer need to wait, purge the timeout id
window.clearTimeout(timeoutID);
// 'myFunction' is defined, invoke it with parameters, if any
myFunction('param1', 'param2');
} else {
// 'myFunction' is undefined, try again in 0.25 secs
timeoutID = window.setTimeout(waitForMyFunction, 250);
}
};
// Initialize
waitForMyFunction();
});
It is tested and working! ;)
Gist: https://gist.github.com/dreamyguy/f319f0b2bffb1f812cf8b7cae4abb47c

Object.defineProperty(window, 'propertyName', {
set: value => {
this._value = value;
// someAction();
},
get: () => this._value
});
or even if you just want this property to be passed as an argument to a function and don't need it to be defined on a global object:
Object.defineProperty(window, 'propertyName', { set: value => someAction(value) })
However, since in your example you seem to want to perform an action upon creation of a node, I would suggest you take a look at MutationObservers.

I have an adaptation of the answer by #dnuttle that I would suggest using.
The advantage of using a try-catch block is that if any part of the code you are trying to execute fails, the whole block fails. I find this useful because it gives you a kind of transaction; everything or nothing gets done.
You should never write code that could end up in an endless loop due to external factors. This is exactly what would happen if you were waiting for a response from an ajax request and the server doesn't respond. I think it's good practice to have a timeout for any questionable loops.
let time = 0; // Used to keep track of how long the loop runs
function waitForElement() {
try {
// I'm testing for an element, but this condition can be
// any reasonable condition
if (document.getElementById('test') === null) {
throw 'error';
}
// This is where you can do something with your variable
// document.getElementById('test').addEventListener....
// or call a function that uses your value
} catch (error) {
// Loop stops executing if not successful within about 5 seconds
if (time > 5000) {
// I return false on failure so I can easily check for failure
return false;
} else {
// Increment the time and call the function again
time += 250;
setTimeout(waitForElement, 250);
}
}
}
// Call the function after the definition, ensures that time is set
waitForElement();

You could have Flash call the function when it's done. I'm not sure what you mean by web services. I assume you have JavaScript code calling web services via Ajax, in which case you would know when they terminate. In the worst case, you could do a looping setTimeout that would check every 100 ms or so.
And the check for whether or not a variable is defined can be just if (myVariable) or safer: if(typeof myVariable == "undefined")

Very late to the party but I want to supply a more modern solution to any future developers looking at this question. It's based off of Toprak's answer but simplified to make it clearer as to what is happening.
<div>Result: <span id="result"></span></div>
<script>
var output = null;
// Define an asynchronous function which will not block where it is called.
async function test(){
// Create a promise with the await operator which instructs the async function to wait for the promise to complete.
await new Promise(function(resolve, reject){
// Execute the code that needs to be completed.
// In this case it is a timeout that takes 2 seconds before returning a result.
setTimeout(function(){
// Just call resolve() with the result wherever the code completes.
resolve("success output");
}, 2000);
// Just for reference, an 'error' has been included.
// It has a chance to occur before resolve() is called in this case, but normally it would only be used when your code fails.
setTimeout(function(){
// Use reject() if your code isn't successful.
reject("error output");
}, Math.random() * 4000);
})
.then(function(result){
// The result variable comes from the first argument of resolve().
output = result;
})
.catch(function(error){
// The error variable comes from the first argument of reject().
// Catch will also catch any unexpected errors that occur during execution.
// In this case, the output variable will be set to either of those results.
if (error) output = error;
});
// Set the content of the result span to output after the promise returns.
document.querySelector("#result").innerHTML = output;
}
// Execute the test async function.
test();
// Executes immediately after test is called.
document.querySelector("#result").innerHTML = "nothing yet";
</script>
Here's the code without comments for easy visual understanding.
var output = null;
async function test(){
await new Promise(function(resolve, reject){
setTimeout(function(){
resolve("success output");
}, 2000);
setTimeout(function(){
reject("error output");
}, Math.random() * 4000);
})
.then(function(result){
output = result;
})
.catch(function(error){
if (error) output = error;
});
document.querySelector("#result").innerHTML = output;
}
test();
document.querySelector("#result").innerHTML = "nothing yet";
On a final note, according to MDN, Promises are supported on all modern browsers with Internet Explorer being the only exception. This compatibility information is also supported by caniuse. However with Bootstrap 5 dropping support for Internet Explorer, and the new Edge based on webkit, it is unlikely to be an issue for most developers.

while (typeof myVar == void(0)) {
if ( typeof myVar != void(0)) {
console.log(myVar);
}
}
This makes use of the typeof operator which only returns undefined if variable is not declared yet. Should be applicable in every type of javascript.

Related

Run Javascript code only once, and with only one spot to put the code

I have an interceptor function that I can put some javascript code into (for swagger ui). I only have one place to put in javascript code and it will be re-run frequently. I need to add a mutating observer in there, and have it only be setup one time.
There are many examples of how to do a Javascript method that will allow your code to be only run once. Here is a popular example: Function in JavaScript that can be called only once
But it assumes that you have somewhere to put the function where it will only be called once.
Is there a way to have my javascript code (not necessarily a function) only be run once? (So I don't end up setting up a bunch of mutating observers?)
To illustrate with an example, here is the code from the question I linked to, but copied in twice to show that it would be run many times:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
The output of this code is:
hello
hello
Because the function is initalized twice, the call to console.log happens twice.
I need some way to have my code only happen once, with only one place to declare it.
What about storing executed value in document ... it is created once at execution ... it could be stored in window (i.e. global var) but I think it is better to be kept in document. I'm not familiar with swagger but docuument should always exits I think. Storing it in localStorage will keep it longer than you may need and you should reset it on each run.
var something = (function() {
return function() {
if (!document.executed) {
document.executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
var something = (function() {
return function() {
if (!document.executed) {
document.executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
For browser environment, you can use localStorage to set the value.
// make sure to initialize with true in your main entry point, otherwise the function would not run for the first time
localStorage.setItem('run-function', true);
const run = localStorage.getItem('run-function');
function test() {
if (run) {
console.log('ran function');
// ran once, set the value to false to prevent dupes
localStorage.setItem('run-function', false);
} else {
console.log('function already called once, exiting');
}
}
test();
if you're on node.js environment you can make use of the global variable
// make sure to initialize with true in your main entry point, otherwise the function would not run for the first time
global.run_function = true;
function test() {
if (global.run_function) {
console.log('ran function');
global.run_function = false;
} else {
console.log('function already called once, exiting');
}
}
test();

JS how to do something only once

For example:
// run this:
alert('Loading...');
// dont run this again:
alert('Loading...');
I don't want to ever repeat that.
How can I do this with convenience (preferably without using booleans)?
The standard way is to use a boolean flag.
But, if you have an aversion to booleans, you can do it by overwriting the function, thus ensuring it literally can never be called again.
function loadButOnlyOnce() {
console.log('This will only happen once!');
loadButOnlyOnce = function() {};
}
loadButOnlyOnce();
loadButOnlyOnce();
Your IDE will probably spew out warnings along the lines of "What are you doing, this is overwriting the function!". But it's your code, and you can do it if you want.
So, now you want a generic solution you can use with different functions? You can do this like so:
function allowOnlyOneCall(f) {
return function() {
f.apply(this, arguments);
f = function() {};
}
}
function myMethod(p1) {
console.log('myMethod is being invoked. p1 = ' + p1);
}
myMethod = allowOnlyOneCall(myMethod);
myMethod(5);
myMethod(5);
Here is one clean implementation you could use so you can avoid the usage of booleans for every single task you don't want to repeat:
var cache = [];
function do_once(task, fn) {
if(cache.indexOf(task) == -1) { // shorthand: !~cache.indexOf(task)
cache.push(task);
return fn();
}
}
Usage:
var alertLoading = alert.bind(null, "Loading...");
do_once("alert_loading", alertLoading); // will run
// later on...
do_once("alert_loading", alertLoading); // will not run again
This works as long as you give each task you don’t want to repeat a different name. Regardless of the function provided as the second argument to do_once, it will not run as long as do_once has already been called using the same task name.
First create a variable to store whether the function has already run or not (such as hasRun). Then simply store your functionality inside an if condition which checks that this variable is false. You would need to update this variable after running the logic for the first time.
This can be seen in the following:
hasRun = false;
document.getElementsByTagName('button')[0].addEventListener('click', function() {
if (!hasRun) {
console.log('Loading...'); // Gets run once
hasRun = true; // Set the flag to true so the conditional doesn't get entered again
} else {
console.log('The function already ran!'); // Runs every subsequent time
}
})
<button>Click me</button>
If you want a factory function that memoizes the result of calling a function with a single primitive value as a parameter, you should use a Map:
const once = (() => fn => {
const cache = new Map()
return value => {
if (!cache.has(value)) {
cache.set(value, fn(value))
}
return cache.get(value)
}
})()
function verboseSquare (value) {
console.log('expensive calculation')
return value * value
}
const squareOnce = once(verboseSquare)
console.log(squareOnce(4))
console.log(squareOnce(4)) // skipped work
console.log(squareOnce(5))
console.log(squareOnce(5)) // skipped work

JavaScript: Pause a function and wait on a global variable

There is a global variable window.listNodes that is an array. This variable is refreshed each 3 seconds and is filled sequentially.
Another function onOpen() is triggered by the user and needs to have the global variable window.listNodes containing 3 elements, not less.
What I seek to do: if the global variable has not a .length equal to 3 then the program waits that the other part of the code fills window.listNodes and then begin again the function onOpen().
socket.onopen = function onOpen() {
if (window.listNodes.length === 3) {
// Do something
} else {
// Wait and when window.listNodes.length === 3:
onOpen();
}
});
};
Is there an easy way to do it? I tried with the function setTimeOut() and with a generator function and the keyword yield but I failed.
Thank you for your precious help :)
This could use setTimeout and a custom interval to wait, for example, 500ms, to do it:
function onOpen() {
if (window.listNodes.length === 3) {
// Do something
} else {
// Wait and when window.listNodes.length === 3:
setTimeout(onOpen, 500);
}
});
};
socket.onopen = onOpen;
With Promises:
function getListNodes() {
return new Promise(function check(resolve) {
if (window.listNodes.length === 3) { return resolve(window.listNodes); }
setTimeout(() => check(resolve), 500);
});
}
socket.onopen = async function() {
const listNodes = await getListNodes();
// use listNodes normally
};
Within an async function, the await keyword will pause the function until the Promise it waits for is resolved (or rejected).
The Promise returned from getListNodes() will retry the lookup every 500 milliseconds, and resolve if it finds that the length is sufficient.
Take note that natively, async functions are only supported in Chrome and in Edge. You will need Babel + Babel polyfill to use them everywhere.

How to do a "for" loop with asynchronous condition in Javascript?

I have this function:
waitForFreeAccnt.prototype.isMemberFree = function () {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return true;
} else {
console.log('it is not free');
return false;
}
});
};
I would like to wait till the account is free for up to 10 seconds with something like that:
var test = function () {
for (var start = 1; start < 10; start++) {
var result = self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
But it doesn't work because self.api.getMemberInfo is asynch call. This is super frustrating with Javascript. Any other language it would be so simple to do. How do I force the for loop to wait for self.isMemberFree() to finish executing before proceeding with the loop?
Also to note, this is not in browser execution so I don't care about anything hanging.
When dealing with asynchronous code, you need to make use of callbacks. That is, if you want to do a() and b() in order but a() does something asynchronously, then you need to call b() from within a() once a() has a result. So not:
a(); // does something asynchronously
b(); // tries to use a()'s result but it isn't available yet
... but rather
a(b); // pass b to a() and a() will call it when ready
function a(callback) {
triggerAsyncFunction(function(result) {
if (result === something)
callback("a just finished");
});
}
Note that a() doesn't refer to b() by name, it just calls whatever function is passed in as an argument.
So applying that to your code, maybe something like this:
waitForFreeAccnt.prototype.isMemberFree = function (cbf) {
var self = this;
self.api.getMemberInfo(function () {
cbf(self.api.connect.accountType === 'FREE');
});
};
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) {
var attempts = 0;
var self = this;
(function attempt() {
self.isMemberFree(function(free) {
if (free)
callback(true);
else if (++attempts < maxAttempts)
setTimeout(attempt, 1000);
else
callback(false);
});
)();
};
this.testMemberXTimes(10, function(isFree) {
// the next part of your code here, or called from here
// because at this point we know we've tested up to
// ten times and isFree tells us the result
});
Note that the way I coded getMemberInfo() it is basically doing the same thing yours was, but instead of returning a boolean it is calling the callback function and passing the same boolean value that you were returning. (I've removed the console.log()s to make the code shorter.)
Note also that you could structure the above to use promises, but the end result will be the same.
You could return a Promise
waitForFreeAccnt.prototype.isMemberFree = function () {
return new Promise((reject, resolve)=>
// set a timeout if api call takes too long
var timeout = setTimeout(()=> reject(Error('API timeout')), 10000);
// make api call
this.api.getMemberInfo(()=> {
clearTimeout(timeout);
resolve(this.api.connect.accountType === 'FREE');
});
);
};
Then use it like this
whatever.isMemberFree().then(isFree=> {
if (isFree)
console.log('it is free');
else
console.log('it is not free');
})
// handle timeout or other errors
.catch(err=> {
console.log(err.message);
});
Building on naomik's answer, if you do it that way you can pretty easily use a for loop with it, using the (most likely) upcoming async/await feature - though it's not part of ES2015.
// Note "async" here! That will make "await" work. It makes the function
// return a promise, which you'll be able to either "await" or
// "test().then" later.
var test = async function () {
for (var start = 1; start < 10; start++) {
// Right here we're using "await" - it makes JavaScript *wait* for
// the promise that comes from self.isMemberFree() to be finished.
// It's really handy because you can use it in loops like "for" and
// "while" without changing the flow of your program!
var result = await self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
For now you'll need to use a transpiler like Babel or Traceur before you can really use async/await, though. It's only supported in Microsoft Edge 14 right now.
And a big emphasis that what is returned from test() isn't whatever you directly return from inside it. If I do this:
var test = async function() { return 15; };
var result = test();
I'm not going to get 15 - I'll get a promise that will resolve as 15:
result.then(function(res) {
console.log(res); // 15
});
// or, use an async function again:
var main = async function() {
console.log(await res); // 15
};
main();
I don't have my work laptop today because it is Sunday, I'm coding this on sublime. Apologise if the syntax is a bit off.
To solve your problem I would recommend changing isMemberFree() to take in a callback function. This is because isMemberFree is async, and you will need a way to report the result after it has done the work.
Then change test function to use setTimeout API to wait a second.
Wrap the function call for isMemberFree() to be in a nested function and call it recursively, that way you'll have synchronize control over the async calls.
Look at the coding example:
waitForFreeAccnt.prototype.isMemberFree = function (done) {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return done(null, true);
} else {
console.log('it is not free');
return done(null, false);
}
});
};
var test = function () {
var testMembership = function(waitAttempt, isFree) {
if (isFree) {
return;
}
else if (waitAttempt > 10) {
// wait exceeded, do something.
return;
}
setTimeout(function() {
self.isMemberFree(function(err, isFree) {
testMembership(waitAttempt+=1, isFree);
});
}, /*total milliseconds in 1sec=*/1000);
}
testMembership(/*WaitAttempts=*/0, /*isFree=*/false);
};
What the above code does is that, presumably something has already been done to the member's account and now test function is called. So it waits for 1 second, then call isMemberFree function, this happens recursively until either isMemberFree() returns true OR the 10 seconds wait has been exceeded.

Simple but difficult. How to force external libs functions run sequentially?

If we have two function in javascript, one slow and one fast. For example:
function slow() {
setTimeout(function() {console.log("slow finished")}, 10000);
}
function fast() {
console.log("fast");
}
And these functions don't have inside of them new structures like promisses (if we do not implement after).
How can we force these functions run in order? For example:
function run() {
slow();
fast();
}
run();
How can we force fast wait slow finishes?
I'm looking a solution that could work inside mobile application browsers, becase of a Apache Cordova project of mine.
Is there a way to do this?
An idea of mine is inject a callback function between the functions.
And this callback is called at the end of the slow function, calling the fast function.
An important thing is I can't (or would not) rewrite the code of the slow and fast functions,
because they will reside inside external libraries.
I'm looking for a solution to countorn this problem as an external observer and manager.
How can we do this?
Edit
He I was a trying to solve the problem merging the answers. No success yet.
I had changed slow but this is not really allowed. I have changed it to se what is happening with a. I couldn't get something interesting because a becomes undefined immediately and not after slow finishes...
var a = "adsfadsfadsf";
function slow() {
setTimeout(function() {console.log("slow done"); console.log("a2", window.a);}, 3000);
}
function fast() {
console.log("a3", window.a);
console.log("fast done");
}
var newSlow = function() {
return new Promise(function(resolve, reject){
window.a = slow();
console.log("a", a);
resolve("Sucess");
});
};
newSlow().then(function(resolve){fast();}, function(reject){console.log("error");});
I have tried with resolve(slow()); no sucess too.
That's a very interesting question. Well I can think of a way where if it is changing some global variable "g" to some value say "true". In that case if you can run them sequentially as,
<script>
var g = false;
var i;
function slow() {
setTimeout(function() {console.log("slow finished");g=true;}, 10000);
}
function fast() {
console.log("fast");
}
function run() {
slow();
i = setInterval(function(){check();},1000);
}
function check(){
if(g){
fast();
clearInterval(i);
}
}
run();
</script>
As in this demo
UPDATE: Something just struck me and I guess we might be able to add a callback function to slow() even if we can't access it directly.
If a function is called without parenthesis then the entire function as a content is returned as a string so we can edit that string by adding fast() to it registering that string as a function using eval().
function run() {
var myFun = slow+"";
myFun = myFun.substring(0,myFun.length-1);
alert(myFun);
myFun += "fast();}";
//to register the string "myFun" as a function
eval(myFun);
slow();
}
So basically our slow() function becomes,
function slow(){
//function code
//the appended function
fast();
}
NOTE: This will not worked in the example given above where GarouDan has deliberately added setTimeout limit to recreate a scenario where the slow() function takes longer time than the fast() function. However, in a real-world scenario I'm sure this approach would definetly work.
You could use the Promise pattern.
Promises are tailor made for situations where various parts of code may run slow or fast or complete in unknowable amounts of time (or not complete at all), while still giving you execution control.
My personal favorite library that implements the Promise pattern is RSVP.
Here is some pseudocode to give you the idea. Run an operation that may take a long time, then run one only when the first has either completed, or handle it's failure.
function doFoo() {
var promise = new RSVP.Promise(function(resolve, reject) {
// do some long-running operation, like retrieve
// data from a slow site...
if (data.Status && data.Status === 200) {
resolve(data);
} else {
reject(data.Error)
}
});
return promise;
}
function doBar() {
var promise = new RSVP.Promise(function(resolve, reject) {
// do some fast operation, like count to 10
for (i = 0; i < 10; i++) {
console.log(i);
}
resolve("");
});
return promise;
}
Now you can call:
function inOrder() {
doFoo().then(function(success) {
doBar();
}).catch (function(failure) {
console.log("oops! " + failure);
}
});
}
This runs doFoo, and ONLY runs doBar after doFoo has completed successfully. Note that you could also run doBar even if doFoo has failed.

Categories

Resources