javascript, wait for something to be true then run action - javascript

Well the title kindof says what I need. Because in Javascript timeouts asynchronous I need to know when something becomes true. I don't want busyloop.
Came up with:
function do_when(predicate, action, timeout_step) {
if (predicate()) {
action();
} else {
setTimeout(do_when, timeout_step, predicate, action, timeout_step);
}
}
Is it good Javascript or can I make better?

Depending on what the predicate is, you might be able to fit your problem into an implementation of the observer pattern. A while back I wrote a blog post about creating JavaScript objects with observable properties. It really depends on what the predicate is, but this might get you most of the way there with code like this:
var observable = createObservable({ propToWatch: false });
observable.observe('propToWatch', function (oldValue, newValue) {
alert('propToWatch has changed from ' + oldValue + ' to ' + newValue);
});
observable.propToWatch(true); // alert pops
Of course, this might be overkill for your example. Since it's never listed out explicitly (n.b. I am not a very good blogger), here's the complete code needed to make this work:
var createMediator = function () {
var events = {};
return {
subscribe: function (eventName, callback) {
events[eventName] = events[eventName] || [];
events[eventName].push(callback);
},
publish: function (eventName) {
var i, callbacks = events[eventName], args;
if (callbacks) {
args = Array.prototype.slice.call(arguments, 1);
for (i = 0; i < callbacks.length; i++) {
callbacks[i].apply(null, args);
}
}
}
};
};
var createObservable = function (properties) {
var notifier = createMediator(), createObservableProperty, observable;
createObservableProperty = function (propName, value) {
return function (newValue) {
var oldValue;
if (typeof newValue !== 'undefined' &&
value !== newValue) {
oldValue = value;
value = newValue;
notifier.publish(propName, oldValue, value);
}
return value;
};
};
observable = {
register: function (propName, value) {
this[propName] = createObservableProperty(propName, value);
this.observableProperties.push(propName);
},
observe: function (propName, observer) {
notifier.subscribe(propName, observer);
},
observableProperties: []
};
for (propName in properties) {
observable.register(propName, properties[propName]);
}
return observable;
};
My observable objects make use internally of a small eventing framework (the createMediator function) I wrote once for a project. (Before realizing jQuery supported custom events. D'oh!) Again, this may or may not be overkill for your need, but I thought it was a fun hack. Enjoy!

It's decent enough, if it's easy enough to read and it works just fine then it's generally good javascript.
Performance-wise, it's generally better to call the function whenever whatever is set to true happens. So in whatever function that executes to make predicate() return true, you could just call action() at the end. But I'm sure that's what you would have done if you could, right?
You could also look at using a callback, where you register a javascript function to a particular variable or function argument and when the function is run it executes whatever function was set to the callback variable.

if your predicate become true when a variable change, here is another solution:
say we want to log 'Big brother is watching you' when value of object a become 2.
function observable (value, condition, callback){
this.value = value;
this.condition = condition;
this.callback = callback;
}
observable.prototype = {
get value () {
return this._value;
},
set value (value) {
this._value = value;
if (this.condition && this.callback && this.condition (value)) {
this.callback (value);
}
}
};
condition = function (value) {
console.log ('condition', value);
return value === 2;
}
callback = function (value) {
console.info ('Big Brother is watching you!');
}
var a = new observable (0, condition, callback);
console.log ('set value to 1');
a.value = 1;
console.log ('set value to 2');
a.value = 2;
console.log ('set value to 3');
a.value = 3;
you can try this exemple in firefox

Related

Add callback to for loop function

I have a function to which I pass an array or an object, then it looks for specific keys and edits their values accordingly,
function iterate(obj, delta) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property],delta);
} else {
if(property === 'unix_time'){
var bee = parseInt(obj[property]);
var b = bee + parseInt(delta);
obj[property] = b;
}
}
}
}
}
Basically, it looks for the "unix_time" key and add a number "delta" to it.
Question: When I call it asynchronous, it becomes undefined, How can I add a callback that I can simply use to determine that the function has finished executing. Or maybe should I add a promise to it?
For example when i run this it returns perfectly
console.log("new one", obj);
iterate(obj, 3600000)
But this is a problem, it becomes undefined
var dd = iterate(obj, 3600000);
res.status(200).send(JSON.stringify(dd));
As mentioned in comments, you function is synchronous and it returns immediately after you call it like this:
var result = iterate(tree, delta);
However, as it's currently written, the result variable will have value of undefined since your iterate function doesn't return anything.
If you have the setup like this:
var obj = {...};
iterate(obj, 3600000)
console.log(obj) // correctly outputs modified object
It will output modified object, since you're not using here the returned value from the function. However, in this scenario:
console.log("new one", iterate(obj, 3600000)); // ouputs `undefined`
the returned value is used and it's undefined.
Using the use case you provided, you can modify the usage like this:
iterate(obj, 3600000);
res.status(200).send(JSON.stringify(obj));
and it will work fine. Or you need to modify iterate to return value. Provide an example of obj so I can write a modification to your iterate function.
Modified the iterate function:
function iterate(obj, delta) {
obj.forEach(function (element) {
if (element.hasOwnProperty('unix_time')) {
element['unix_time'] = parseInt(element['unix_time']) + parseInt(delta);
}
});
return obj;
}
I don't know I understand your question. But, if you want to use a callback, you should split this funcion in two. One for main operation and another for recursivity.
i.e.
function iterate(obj, delta, callback) {
interate_recursive(obj, delta);
if(typeof callback != 'undefined')
return callback();
else return obj;
}
function interate_recursive(obj,delta){
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property],delta);
} else {
if(property === 'unix_time'){
var bee = parseInt(obj[property]);
var b = bee + parseInt(delta);
obj[property] = b;
}
}
}
}
}

get element attribute value in Protractor

I'm writing a Protractor test that has to wait for an element attribute to have a non-empty value and then I want to return that value to the caller function. This has proven to be more difficult to write than I expected!
I am able to correctly schedule a browser.wait() command to wait for the element attribute to have a non-empty value and I have verified that this value is in fact what I am expecting to get inside the callback function, but for some reason, I am not able to return that value outside of the callback function and onto the rest of the test code.
Here is how my code looks like:
function test() {
var item = getItem();
console.log(item);
}
function getItem() {
var item;
browser.wait(function() {
return element(by.id('element-id')).getAttribute('attribute-name').then(function(value) {
item = value;
// console.log(item);
return value !== '';
});
});
return item;
}
I can tell that the order of execution is not as I expect it to be, because when I uncomment the console.log() call inside the callback function, I see the expected value printed out. However, the same call in the test() function prints 'undefined'.
What is going on here? What am I missing? How can I get the attribute value out of the callback function properly?
I appreciate your help.
I would not combine the wait and the getting attribute parts - logically these are two separate things, keep them separate:
browser.wait(function() {
return element(by.id('element-id')).getAttribute("attribute").then(function(value) {
item = value;
// console.log(item);
return value !== '';
});
});
element(by.id('element-id')).getAttribute("attribute").then(function (value) {
console.log(value);
});
Note that, you may simplify the wait condition this way:
var EC = protractor.ExpectedConditions;
var elm = $('#element-id[attribute="expected value"]');
browser.wait(EC.presenceOf(elm), 5000);
elm.getAttribute("attribute").then(function (value) {
console.log(value);
});
Just FYI, you may have solved your current problem with the deferred:
function test() {
getItem().then(function (value) {
console.log(value);
});
}
function getItem() {
var item = protractor.promise.defer();
browser.wait(function() {
return element(by.id('element-id')).getAttribute('attribute').then(function(value) {
var result = value !== '';
if (result) {
item.fulfill(value);
}
return result;
});
});
return item.promise;
}
After doing some more reading about how protractor works with promises and schedules/registers them with the control flow, I found an easier work-around close to the first solution #alecxe provided. Here it goes:
function test() {
var item = getItem().then(function(item) {
console.log(item);
});
}
function getItem() {
return browser.wait(function() {
return element(by.id('element-id')).getAttribute('attribute-name').then(function(value) {
return value;
});
});
}
Since browser.wait() returns a promise itself, it can be chained with another then() inside the caller and this way the right order of execution is guaranteed.

Making a javascript function async with callback

Previously I had
MyClass.prototype.method1 = function(data1) {
return this.data111.push(data1);
};
MyClass.prototype.method2 = function(i) {
var data = this.method1(i);
if (data.condition1 != null) {
data.onEvent1(this);
}
return $(data.element).someMethod123("data123");
};
MyClass.prototype.method3 = function() {
var data1 = this.method1(this._data1);
return this.someMethod123(step.data1);
};
MyClass.prototype.ended = function() {
return !!this.getState("end");
};
MyClass.prototype.getState = function(key) {
var value = $.cookie(key);
this._options.afterGetState(key, value);
return value;
};
How do I make async using callback functions? I guess it should be so:
MyClass.prototype.method1 = function(data1, callback) {
if(callback){
callback(this.data111.push(data1));
}
else{
return this.data111.push(data1);
}
};
MyClass.prototype.method2 = function(i, callback) {
var data = this.method1(i);
if (data.condition1 != null) {
data.onEvent1(this);
}
if(callback){
callback($(data.element).someMethod123("data123"));
}
else{
return $(data.element).someMethod123("data123");
}
};
MyClass.prototype.method3 = function(callback) {
var data1 = this.method1(this._data1);
if(callback){
callback(this.someMethod123(step.data1));
}
else{
return this.someMethod123(step.data1);
}
};
MyClass.prototype.ended = function(callback) {
if(callback){
callback(!!this.getState("end", /*what should be here and what should it does?*/));
}
};
MyClass.prototype.getState = function(key, callback) {
var oldThis = this;
setTimeout(function(){
value = $.cookie(key);
callback(value, oldThis);
oldThis._options.afterGetState(key, value);
},
0);
};
I definitely have missed something because I never used async functions in javascript before. So it that?
And, as I understood, to make a functions async, I basically should add one more parameter as a callback function and get rid of return, should not I?
Just pass on the callback:
MyClass.prototype.ended = function(callback) {
this.getState("end", callback);
};
You should do this in your other functions too and I'd suggest to stick to one interface. I.e. either return the value directly (if possible) or use callbacks.
Only those methods that do asynchronous tasks need callback style. There is no reason to use it for method1, method2 and method3.
getState now is the actual asynchronous method. Using ajax/setTimeout/whatever is quite obvious in here. Yet, I can spot one mistake: The callback call should always be the last statement, just as you won't do anything after a return statement. Even though you can, better call back after setting the internal options object:
…
oldThis._options.afterGetState(key, value);
callback(value, oldThis);
Now, the ended method. Since it uses the async getState, it will become asynchronous itself and you need to use callback style (notice that getState() will not return the value). So you will call getState, and when that calls back you will transform the result and pass it to your own callback:
MyClass.prototype.ended = function(callback) {
this.getState("end", function ownCallback(state) {
var result = !!state; // or whatever you need to do
callback(result);
});
};
// and if you don't need to do anything with the result, you can leave out ownCallback:
MyClass.prototype.ended = function(callback) {
this.getState("end", callback);
};

Function in JavaScript that can be called only once

I need to create a function which can be executed only once, in each time after the first it won't be executed. I know from C++ and Java about static variables that can do the work but I would like to know if there is a more elegant way to do this?
If by "won't be executed" you mean "will do nothing when called more than once", you can create a closure:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
// do something
}
};
})();
something(); // "do something" happens
something(); // nothing happens
In answer to a comment by #Vladloffe (now deleted): With a global variable, other code could reset the value of the "executed" flag (whatever name you pick for it). With a closure, other code has no way to do that, either accidentally or deliberately.
As other answers here point out, several libraries (such as Underscore and Ramda) have a little utility function (typically named once()[*]) that accepts a function as an argument and returns another function that calls the supplied function exactly once, regardless of how many times the returned function is called. The returned function also caches the value first returned by the supplied function and returns that on subsequent calls.
However, if you aren't using such a third-party library, but still want a utility function (rather than the nonce solution I offered above), it's easy enough to implement. The nicest version I've seen is this one posted by David Walsh:
function once(fn, context) {
var result;
return function() {
if (fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
I would be inclined to change fn = null; to fn = context = null;. There's no reason for the closure to maintain a reference to context once fn has been called.
Usage:
function something() { /* do something */ }
var one_something = once(something);
one_something(); // "do something" happens
one_something(); // nothing happens
[*] Be aware, though, that other libraries, such as this Drupal extension to jQuery, may have a function named once() that does something quite different.
Replace it with a reusable NOOP (no operation) function.
// this function does nothing
function noop() {};
function foo() {
foo = noop; // swap the functions
// do your thing
}
function bar() {
bar = noop; // swap the functions
// do your thing
}
Point to an empty function once it has been called:
function myFunc(){
myFunc = function(){}; // kill it as soon as it was called
console.log('call once and never again!'); // your stuff here
};
<button onClick=myFunc()>Call myFunc()</button>
Or, like so:
var myFunc = function func(){
if( myFunc.fired ) return;
myFunc.fired = true;
console.log('called once and never again!'); // your stuff here
};
// even if referenced & "renamed"
((refToMyfunc)=>{
setInterval(refToMyfunc, 1000);
})(myFunc)
UnderscoreJs has a function that does that, underscorejs.org/#once
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
Talking about static variables, this is a little bit like closure variant:
var once = function() {
if(once.done) return;
console.log('Doing this once!');
once.done = true;
};
once(); // Logs "Doing this once!"
once(); // Logs nothing
You could then reset a function if you wish:
once.done = false;
once(); // Logs "Doing this once!" again
You could simply have the function "remove itself"
​function Once(){
console.log("run");
Once = undefined;
}
Once(); // run
Once(); // Uncaught TypeError: undefined is not a function
But this may not be the best answer if you don't want to be swallowing errors.
You could also do this:
function Once(){
console.log("run");
Once = function(){};
}
Once(); // run
Once(); // nothing happens
I need it to work like smart pointer, if there no elements from type A it can be executed, if there is one or more A elements the function can't be executed.
function Conditional(){
if (!<no elements from type A>) return;
// do stuff
}
var quit = false;
function something() {
if(quit) {
return;
}
quit = true;
... other code....
}
simple decorator that easy to write when you need
function one(func) {
return function () {
func && func.apply(this, arguments);
func = null;
}
}
using:
var initializer= one( _ =>{
console.log('initializing')
})
initializer() // 'initializing'
initializer() // nop
initializer() // nop
try this
var fun = (function() {
var called = false;
return function() {
if (!called) {
console.log("I called");
called = true;
}
}
})()
From some dude named Crockford... :)
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(
this,
arguments
);
};
}
Reusable invalidate function which works with setInterval:
var myFunc = function (){
if (invalidate(arguments)) return;
console.log('called once and never again!'); // your stuff here
};
const invalidate = function(a) {
var fired = a.callee.fired;
a.callee.fired = true;
return fired;
}
setInterval(myFunc, 1000);
Try it on JSBin: https://jsbin.com/vicipar/edit?js,console
Variation of answer from Bunyk
Here is an example JSFiddle - http://jsfiddle.net/6yL6t/
And the code:
function hashCode(str) {
var hash = 0, i, chr, len;
if (str.length == 0) return hash;
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
var onceHashes = {};
function once(func) {
var unique = hashCode(func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]);
if (!onceHashes[unique]) {
onceHashes[unique] = true;
func();
}
}
You could do:
for (var i=0; i<10; i++) {
once(function() {
alert(i);
});
}
And it will run only once :)
Initial setup:
var once = function( once_fn ) {
var ret, is_called;
// return new function which is our control function
// to make sure once_fn is only called once:
return function(arg1, arg2, arg3) {
if ( is_called ) return ret;
is_called = true;
// return the result from once_fn and store to so we can return it multiply times:
// you might wanna look at Function.prototype.apply:
ret = once_fn(arg1, arg2, arg3);
return ret;
};
}
If your using Node.js or writing JavaScript with browserify, consider the "once" npm module:
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
If you want to be able to reuse the function in the future then this works well based on ed Hopp's code above (I realize that the original question didn't call for this extra feature!):
var something = (function() {
var executed = false;
return function(value) {
// if an argument is not present then
if(arguments.length == 0) {
if (!executed) {
executed = true;
//Do stuff here only once unless reset
console.log("Hello World!");
}
else return;
} else {
// otherwise allow the function to fire again
executed = value;
return;
}
}
})();
something();//Hello World!
something();
something();
console.log("Reset"); //Reset
something(false);
something();//Hello World!
something();
something();
The output look like:
Hello World!
Reset
Hello World!
A simple example for turning on light only once.
function turnOnLightOnce() {
let lightOn = false;
return function () {
if (!lightOn) {
console.log("Light is not on...Turning it on for first and last time");
lightOn = true;
}
};
}
const lightOn = turnOnLightOnce();
lightOn() // Light is not on...Turning it on for first and last time
lightOn()
lightOn()
lightOn()
lightOn()
https://codesandbox.io/s/javascript-forked-ojo0i?file=/index.js
This happens due to closure in JavaScript.
function once (fn1) {
var ran = false
var memo = null
var fn = function(...args) {
if(ran) {return memo}
ran = true
memo = fn1.apply(null, args)
return memo
}
return fn
}
I'm using typescript with node and it was #I Hate Lazy's answer that inspired me. I just assigned my function to a noop function.
let printName = (name: string) => {
console.log(name)
printName = () => {}
}
printName('Sophia') // Sophia
printName('Nico') // Nothing Happens
https://jsbin.com/yuzicek/edit?js,console
FOR EVENT HANDLER
If the function is a callback for an event listener, there is already a built-in option in the addEventListner method for just executing the callback once.
It can accept 3 parameters
Type
callback
options
options is an object that has a property called once
ex:
const button = document.getElementById('button');
const callbackFunc = () => {
alert('run')
}
button.addEventListener('click', callbackFunc, { once: true })
<button id="button">Click Once</button>
Trying to use underscore "once" function:
var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.
http://underscorejs.org/#once
var init = function() {
console.log("logges only once");
init = false;
};
if(init) { init(); }
/* next time executing init() will cause error because now init is
-equal to false, thus typing init will return false; */
if (!window.doesThisOnce){
function myFunction() {
// do something
window.doesThisOnce = true;
};
};
If you're using Ramda, you can use the function "once".
A quote from the documentation:
once Function
(a… → b) → (a… → b)
PARAMETERS
Added in v0.1.0
Accepts a function fn and returns a function that guards invocation of fn such that fn can only ever be called once, no matter how many times the returned function is invoked. The first value calculated is returned in subsequent invocations.
var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11
keep it as simple as possible
function sree(){
console.log('hey');
window.sree = _=>{};
}
You can see the result
JQuery allows to call the function only once using the method one():
let func = function() {
console.log('Calling just once!');
}
let elem = $('#example');
elem.one('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p>Function that can be called only once</p>
<button id="example" >JQuery one()</button>
</div>
Implementation using JQuery method on():
let func = function(e) {
console.log('Calling just once!');
$(e.target).off(e.type, func)
}
let elem = $('#example');
elem.on('click', func);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p>Function that can be called only once</p>
<button id="example" >JQuery on()</button>
</div>
Implementation using native JS:
let func = function(e) {
console.log('Calling just once!');
e.target.removeEventListener(e.type, func);
}
let elem = document.getElementById('example');
elem.addEventListener('click', func);
<div>
<p>Functions that can be called only once</p>
<button id="example" >ECMAScript addEventListener</button>
</div>
Tossing my hat in the ring for fun, added advantage of memoizing
const callOnce = (fn, i=0, memo) => () => i++ ? memo : (memo = fn());
// usage
const myExpensiveFunction = () => { return console.log('joe'),5; }
const memoed = callOnce(myExpensiveFunction);
memoed(); //logs "joe", returns 5
memoed(); // returns 5
memoed(); // returns 5
...
You can use IIFE. IIFE means Immediately Invoked Function Expression and the result is to call a function only once by the time is created.
Your code will be like this:
(function () {
//The code you want to execute only one time etc...
console.log("Hello world");
})()
Additionally, this way the data in the function remains encapsulated.
Of course and you can return values from the function and stored them into a new variable, by doing:
const/let value = (function () {
//The code you want to execute only one time etc...
const x = 10;
return x;
})()
function x()
{
let a=0;
return function check()
{
if(!a++)
{
console.log("This Function will execute Once.")
return;
}
console.log("You Can't Execute it For the Second Time.")
return;
}
}
z=x()
z() //Op - This Function will execute once
z() //OP - You can't Execute it for the second time.
I find it useful to just have a simple function that just returns true once, so you can keep the side effects higher up.
let once = () => !! (once = () => false);
once() // true
once() // false
Use like this:
if (once()) {
sideEffect()
}
This exploits the fact that you can coerce an assignment expression to return true while changing the same function into a function that returns false.
If you must have it execute a function, it can be adapted using a ternary:
let once = (x) => !! (once = () => false) ? x() : false;
Now it accepts a single function as an argument. Fun fact, the second false is never reached.
// This is how function in JavaScript can be called only once
let started = false;
if (!started) {
start() { // "do something" }
}
started = true;
}

How can I make a program wait for a variable change in javascript?

I want to force a JavaScript program to wait in some particular points of its execution until a variable has changed. Is there a way to do it? I have already found an extension that is called "narrative JavaScript" that force the program to wait until an event to happen. Is there a way to create a new event, a "variable change event" for example that behaves like onclick event..
Edit 2018: Please look into Object getters and setters and Proxies. Old answer below:
a quick and easy solution goes like this:
var something=999;
var something_cachedValue=something;
function doStuff() {
if(something===something_cachedValue) {//we want it to match
setTimeout(doStuff, 50);//wait 50 millisecnds then recheck
return;
}
something_cachedValue=something;
//real action
}
doStuff();
JavaScript interpreters are single threaded, so a variable can never change, when the code is waiting in some other code that does not change the variable.
In my opinion it would be the best solution to wrap the variable in some kind of object that has a getter and setter function. You can then register a callback function in the object that is called when the setter function of the object is called. You can then use the getter function in the callback to retrieve the current value:
function Wrapper(callback) {
var value;
this.set = function(v) {
value = v;
callback(this);
}
this.get = function() {
return value;
}
}
This could be easily used like this:
<html>
<head>
<script type="text/javascript" src="wrapper.js"></script>
<script type="text/javascript">
function callback(wrapper) {
alert("Value is now: " + wrapper.get());
}
wrapper = new Wrapper(callback);
</script>
</head>
<body>
<input type="text" onchange="wrapper.set(this.value)"/>
</body>
</html>
I would recommend a wrapper that will handle value being changed. For example you can have JavaScript function, like this:
​function Variable(initVal, onChange)
{
this.val = initVal; //Value to be stored in this object
this.onChange = onChange; //OnChange handler
//This method returns stored value
this.GetValue = function()
{
return this.val;
}
//This method changes the value and calls the given handler
this.SetValue = function(value)
{
this.val = value;
this.onChange();
}
}
And then you can make an object out of it that will hold value that you want to monitor, and also a function that will be called when the value gets changed. For example, if you want to be alerted when the value changes, and initial value is 10, you would write code like this:
var myVar = new Variable(10, function(){alert("Value changed!");});
Handler function(){alert("Value changed!");} will be called (if you look at the code) when SetValue() is called.
You can get value like so:
alert(myVar.GetValue());
You can set value like so:
myVar.SetValue(12);
And immediately after, an alert will be shown on the screen. See how it works: http://jsfiddle.net/cDJsB/
The question was posted long time ago, many answers pool the target periodically and produces unnecessary waste of resources if the target is unchanged. In addition, most answers do not block the program while waiting for changes as required by the original post.
We can now apply a solution that is purely event-driven.
The solution uses onClick event to deliver event triggered by value change.
The solution can be run on modern browsers that support Promise and async/await. If you are using Node.js, consider EventEmitter as a better solution.
<!-- This div is the trick. -->
<div id="trick" onclick="onTrickClick()" />
<!-- Someone else change the value you monitored. In this case, the person will click this button. -->
<button onclick="changeValue()">Change value</button>
<script>
// targetObj.x is the value you want to monitor.
const targetObj = {
_x: 0,
get x() {
return this._x;
},
set x(value) {
this._x = value;
// The following line tells your code targetObj.x has been changed.
document.getElementById('trick').click();
}
};
// Someone else click the button above and change targetObj.x.
function changeValue() {
targetObj.x = targetObj.x + 1;
}
// This is called by the trick div. We fill the details later.
let onTrickClick = function () { };
// Use Promise to help you "wait". This function is called in your code.
function waitForChange() {
return new Promise(resolve => {
onTrickClick = function () {
resolve();
}
});
}
// Your main code (must be in an async function).
(async () => {
while (true) { // The loop is not for pooling. It receives the change event passively.
await waitForChange(); // Wait until targetObj.x has been changed.
alert(targetObj.x); // Show the dialog only when targetObj.x is changed.
await new Promise(resolve => setTimeout(resolve, 0)); // Making the dialog to show properly. You will not need this line in your code.
}
})();
</script>
What worked for me (I looked all over the place and ended up using someone's jsfiddler / very slightly modifying it - worked nicely) was to set that variable to an object with a getter and setter, and the setter triggers the function that is waiting for variable change.
var myVariableImWaitingOn = function (methodNameToTriggerWhenChanged){
triggerVar = this;
triggerVar.val = '';
triggerVar.onChange = methodNameToTriggerWhenChanged;
this.SetValue(value){
if (value != 'undefined' && value != ''){
triggerVar.val = value; //modify this according to what you're passing in -
//like a loop if an array that's only available for a short time, etc
triggerVar.onChange(); //could also pass the val to the waiting function here
//or the waiting function can just call myVariableImWaitingOn.GetValue()
}
};
this.GetValue(){
return triggerVar.val();
};
};
Example for a more functional promise (async/await) based approach:
var delay = require('delay');
var obj = {
k: null
};
function notAwaitable() {
return obj.k;
}
async function waitFor(f) {
let r = f();
while (!r) {
console.log('Not yet, waiting more');
await delay(1000);
r = f();
}
return r;
}
(async function() {
await delay(5000);
obj.k = 42;
})();
(async function() {
let result = await waitFor(notAwaitable);
console.log(result);
})();
You can use properties:
Object.defineProperty MDN documentation
Example:
function def(varName, onChange) {
var _value;
Object.defineProperty(this, varName, {
get: function() {
return _value;
},
set: function(value) {
if (onChange)
onChange(_value, value);
_value = value;
}
});
return this[varName];
}
def('myVar', function (oldValue, newValue) {
alert('Old value: ' + oldValue + '\nNew value: ' + newValue);
});
myVar = 1; // alert: Old value: undefined | New value: 1
myVar = 2; // alert: Old value: 1 | New value: 2
Super dated, but certainly good ways to accomodate this. Just wrote this up
for a project and figured I'd share. Similar to some of the others, varied in style.
var ObjectListener = function(prop, value) {
if (value === undefined) value = null;
var obj = {};
obj.internal = value;
obj.watcher = (function(x) {});
obj.emit = function(fn) {
obj.watch = fn;
};
var setter = {};
setter.enumerable = true;
setter.configurable = true;
setter.set = function(x) {
obj.internal = x;
obj.watcher(x);
};
var getter = {};
getter.enumerable = true;
getter.configurable = true;
getter.get = function() {
return obj.internal;
};
return (obj,
Object.defineProperty(obj, prop, setter),
Object.defineProperty(obj, prop, getter),
obj.emit, obj);
};
user._licenseXYZ = ObjectListener(testProp);
user._licenseXYZ.emit(testLog);
function testLog() {
return function() {
return console.log([
'user._licenseXYZ.testProp was updated to ', value
].join('');
};
}
user._licenseXYZ.testProp = 123;
Alternatively, you can make a function that executes tasks based on the value of its "Static" variables, example below:
<!DOCTYPE html>
<div id="Time_Box"> Time </div>
<button type="button" onclick='Update_Time("on")'>Update Time On</button>
<button type="button" onclick='Update_Time("off")'>Update Time Off</button>
<script>
var Update_Time = (function () { //_____________________________________________________________
var Static = []; //"var" declares "Static" variable as static object in this function
return function (Option) {
var Local = []; //"var" declares "Local" variable as local object in this function
if (typeof Option === 'string'){Static.Update = Option};
if (Static.Update === "on"){
document.getElementById("Time_Box").innerText = Date();
setTimeout(function(){Update_Time()}, 1000); //update every 1 seconds
};
};
})();
Update_Time('on'); //turns on time update
</script>
No you would have to create your own solution. Like using the Observer design pattern or something.
If you have no control over the variable or who is using it, I'm afraid you're doomed.
EDIT: Or use Skilldrick's solution!
Mike

Categories