Template method pattern with setTimeout in JavaScript - javascript

I have a close function that will close some instance. The class that includes the function allows derived classes to override close. Here, I want to make sure that close always calls dispose even in derived classes. I achieve this by the following.
function close() {
closeCore();
dispose();
}
function closeCore() {
// derived class can override this method.
}
This works fine, but I have one case where I want to perform CSS animation before I dispose the instance. This is what I do.
function close () {
instance.classList.add("fancy-animation-that-takes-800ms");
setTimeout(function () {
dispose();
},800);
}
But as soon as I do this, the template pattern I use cannot be applied. Is there a way to make sure the close function always call dispose in the second example?

You might have close expect an object be returned from closeCore which had parameters like these:
return {timeout: 800, callback: function () {/*....will execute after a timeout..*/}};
or:
return {callback: function () { /*...will be immediately executed...*/}};
or:
return function () { /*...will be immediately executed...*/};
...and then call their timeout for them (if any), and then after your timeout executed their callback, then call dispose for them.
The relevant part of your close code might look like:
function close() {
var ccObj = closeCore();
var ccIsObj = ccObj && typeof ccObj === 'object';
var callback = typeof ccObj === 'function' ? ccObj : (ccIsObj ? ccObj.callback : null);
if (ccIsObj && ccObj.timeout) {
if (!callback) {
throw 'You must implement a callback when supplying a timeout';
}
setTimeout(function () {
callback();
dispose();
}, ccObj.timeout);
}
else {
if (callback) {callback();}
dispose();
}
}
But if you want to allow the user to make arbitrary asynchronous calls of their own (such as Ajax), while you could instead allow the returning of a promise to which you added the dispose call, you wouldn't have a guarantee that the deriver would ensure the promise completed. You could automatically cancel the promise after a certain period of time, but you couldn't magically know when the deriving code was meant to finish unless again you abstracted this.

Related

Access and modify website variables using Javascript [duplicate]

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.

Understanding Javascript's _this, different types of function calls and how to call a function inside a function?

I am learning how to reverse engineer an existing javascript code and I've ran into a few issues which is due to my lack of understanding how core javascript works. The code is below along with a screen shot of the comments I have.
The code starts out with var warper being declared.
And then warper variable equals a function inside a function? Why isn't it the usual call of function Warper(), but its inside another function?
I noticed the use of _this. How is that different from the regular this that is usually used?
The #btn-submit id is set to activate when it is clicked on. I can see that it calls the click_submit function, but why is it Warper.prototype.click_submit instead of just click_submit()?
And my final question, which is what I really want to do is call the click_submit function via js without having to click on the #btn-submit button.
Question: How do I call the warper.click_submit function using js without the need to click a button? I'm trying to integrate this into another piece of my code.
I tried warper.prototype.click_submit and it doesnt do anything. I'm assuming its because its inside a function in a function?
(function() {
var Warper;
Warper = (function() {
function Warper() {
this.check_compatibility();
this.attach_ux();
if (window.SALT_DEFAULT != null) {
$('#salt').val(window.SALT_DEFAULT);
$('#salt').attr('disabled', true);
$('.salt-label').text('Prefilled salt');
}
}
Warper.prototype.check_compatibility = function() {
if (typeof Int32Array === "undefined" || Int32Array === null) {
return $('.form-container').html('<p>\n Sorry, but your browser is too old to run WarpWallet, which requires Int32Array support.\n</p>');
}
};
Warper.prototype.attach_ux = function() {
$('#btn-submit').on('click', (function(_this) {
return function() {
return _this.click_submit();
};
})(this));
$('#btn-reset').on('click', (function(_this) {
return function() {
return _this.click_reset();
};
})(this));
return $('.what-salt').on('click', (function(_this) {
return function() {
return $('.salt-explanation').toggle();
};
})(this));
};
Warper.prototype.click_submit = function() {
$('#btn-submit').attr('disabled', true).html('Running...');
$('#btn-reset').attr('disabled', true).html('Running...');
$('#passphrase, #salt, checkbox-salt-confirm').attr('disabled', true);
$('.progress-pbkdf2, .progress-scrypt').html('');
$('.progress-form').show();
return warpwallet.run({
passphrase: $('#passphrase').val(),
salt: $('#salt').val(),
progress_hook: (function(_this) {
return function(o) {
return _this.progress_hook(o);
};
})(this),
params: window.params
}, (function(_this) {
return function(res) {
$('#passphrase, #checkbox-salt-confirm').attr('disabled', false);
if (window.SALT_DEFAULT == null) {
$('#salt').attr('disabled', false);
}
$('#private-key').val(res["private"]);
_this.write_qrs(res["public"], res["private"]);
return console.log;
};
})(this));
}; //click_submit
return Warper;
})(); // Warper End
$(function() {
return new Warper();
});
}).call(this); // End Function
The code starts out with var warper being declared.
And then warper variable equals a function inside a function? Why isn't it the usual call of function Warper(), but its inside
another function?
It is inside another function to create it's own scope. This practice is used mainly to separate non relative code and prevent global variables.
I noticed the use of _this. How is that different from the regular this that is usually used?
_this is just a variable that is set to point to outer this. Everytime you call a function it has own this (depending on how you called the function). So if define callback inside another function, and you need to refer to this of that outer ("another") function, you can save it temporarily to variable. This temporarily variables are usually referred to as _this, that or self.
The #btn-submit id is set to activate when it is clicked on. I can see that it calls the click_submit function, but why is it
Warper.prototype.click_submit instead of just click_submit()?
If you define function on prototype, every instance will use that same function. If you would define it on this.clik_submit = function(){...} then every instance would have to have it's own copy of that function. Last option is to just define function click_submit(){...} inside scope, but then the function wouldn't be accessible from outside the scope.
And my final question, which is what I really want to do is call the click_submit function via js without having to click on the
btn-submit button.
You need to gain access to warper instance to be able to call the click_submit function. Without it (and without being able to update the code) it is not possible to call it. But you could consider creating click event on button yourself which would trigger the function. Using jquery this is as easy as $("#btn-submit").click();
Warper is an object.
Warper.prototype.attach_ux = function() {
$('#btn-submit').on('click', (function(_this) { //2
return function() {
return _this.click_submit(); //3
};
})(this));//1.
on //1 'this' refers to the Warper object and is bound to the scope of Warper.prototype.attach_ux function.
on //2 _this is the name of argument which refers to the value passed at //1.
Since //1 refers to the Warper object the click_submit method is called.
This is IIFE pattern of function invocation.
If you were to call
$('#btn-submit').on('click', function(_this) {
return function() {
return _this.click_submit();
};
})
Here _this would refer to the click event and we would not be able to access the defined method conviniently. But we are able to pass the Warper object using IIFE pattern and access it easily.

Retrieve data from indexedDB and return value to function calling

I have share variable between javascript function which is asynchronous. One of them is main thread and another is event based. I want to return value when event is completed.
This is the code:
completeExecution = false; // Shared Variable (Global Variable)
indexDBdata = {}; // Shared Variable (Global Variable)
function getPermission(key) {
var permission_data={};
if(exist_in_local) {
indexdbConnection.getRecordByKey('userPermission',permitKey,function(data){
indexDBdata=data; // Before its complete function return value
});
} else {
// make ajax call & its working fine
}
return permission_data;
}
//get Data from IndexedDB
getRecordByKey:function(tableName,key,readRecords){
if(isEmptyOrNull(readRecords)){
console.log("callback function should not be empty");
return;
}
if(isEmptyOrNull(tableName)){
console.log("table name should not be empty");
return;
}
var returnObj={};
var isSuccessfull=false;
if(this.dbObject.objectStoreNames.contains(tableName)){
var transaction=this.dbObject.transaction(tableName);
var objectStore = transaction.objectStore(tableName);
objectStore.get(key).onsuccess = function(event) {
returnObj=event.target.result;
};
**//Return object after this events compelte**
transaction.oncomplete = function(evt) {
completeExecution=true;
indexDBdata=returnObj;
readRecords(returnObj);
};
transaction.onerror = function(evt) {
completeExecution=true;
indexDBdata={status:'404'};
readRecords("Table Not found");
};
} else {
completeExecution=true;
indexDBdata={status:'404'};
readRecords("Table Not found");
}
}
Problem is while retrieving data from indexedDB it always returns {} (empty object). I want to synchronised event thread and main thread or wait for event to be completed. I don't want to directly manipulate DOM on callbacks I have to return value.
If you have solution to above problem or any other trick then please help me.
Thanks in advance.
I don't find the question very clear, but if I understand it, then you need to learn more about writing asynchronous javascript. In general, functions that call callback functions are void (they return an undefined value). If you want to use the results of two callback functions together, then you will want to chain them so that upon the completion of the first function, which calls its callback function, the callback function then calls the second function which then calls the second callback. So there are four function calls involved. You will want to place the processing logic within the context of the successive callback function, instead of continuing the logic outside of the function and trying to use its return value.
In other words, instead of trying to do this:
function a() {}
function b() {}
var aresult = a();
var bresult = b(aresult);
// processing of both a and b
You would want to try and do something like following:
function a(acallback) {
acallback(...);
}
function b(bcallback) {
bcallback(...);
}
a(function(...) {
b(function(...) {
// all processing of both a and b
});
});

Testing JavaScript callback functions with jasmine

I have the following functions:
function getPersonData(id) {
retrieveData(
id,
function(person) {
if(person.name) {
displayPerson(person);
}
}
}
function retrieveData(id, successCallBack) {
executeRequest(id, {
success: successCallBack
});
}
getPersonData retrieves a person's information based on the id. It in turn calls retrieveData by passing in the id and a successCallBack function.
retrieveData takes the id and successCallBack and calls another function, executeRequest, which gets the data and passes back a person object.
I am trying to test getPersonData and have the following spec set up
describe("when getPersonData is called with the right person id", function() {
beforeEach(function() {
spyOn(projA.data, "retrieveData").and.returnValue(
{
'name': 'john'
}
);
spyOn(projA.data, "displayPerson");
projA.data.getPersonData("123");
});
it("displays the person's details", function() {
expect(projA.data.displayPerson).toHaveBeenCalled();
);
});
But when the spec is executed the displayPerson method isn't called. This is because the person data being passed back from the success callBack function(person) isn't being passed in even though I have mocked retrieveData to return a result.
My question is:
Is this the right way to test callBack functions? Either way what am I doing wrong?
Ok, so jasmine is tricky in a lot of subtle ways and I think there's two main issues with your code
You have way too many asynchronous calls wrapped in each other. Which is by itself not a problem, but it makes testing in JASMINE hell of a lot harder. For example, what is the point of having a retrieveData function which just calls executeRequest function with the exact same parameters but in a slightly different way.
I rewrote your getPersonData to be like this
function getPersonData(id) {
// this is needed to properly spy in Jasmine
var self = this;
//replaced retrieveData with just execute request
// self is required to properly spy in Jasmine
self.executeRequest(id, {
success: function(person) {
if (person.name) {
self.displayPerson(person);
}
}
})
}
//I don't know what exactly executeRequest does
//but I took the liberty to just make it up for this example
function executeRequest(id, callback) {
callback.success({
name: id
});
}
//I also assumed that projA would look something like this
var projA = {
data: {
getPersonData: getPersonData,
retrieveData: retrieveData,
displayPerson: displayPerson,
executeRequest: executeRequest
}
};
2. In order to test asynchronous code in Jasmine, you need to include a done callback with the test. Also, if you expect a callback function to fire automatically, you need to set it up within a setTimeout function, otherwise it will never fire. Here's an adjusted example:
describe("when getPersonData is called with the right person id", function() {
beforeEach(function() {
//you should not spyOn retriveData or executeCode because it will override the function you wrote and will never call displayPerson
// you should only have spies on the methods you are testing otherwise they will override other methods
spyOn(projA.data, "displayPerson");
});
it("displays the person's details", function(done) {
// it's better to call the function within specific test blocks so you have control over the test
projA.data.getPersonData("123");
// at this point, it will just have called the getPersonData but will not call executeRequest
setTimeout(function() {
//this block will just call executeRequest
setTimeout(function() {
//this block will finally call displayPerson
expect(projA.data.displayPerson).toHaveBeenCalled();
//tell jasmine the test is done after this
done();
})
})
});
})

understanding event handling and callbacks in javascript

I was messing around with IndexedDB and I realised that I don't really get event handling in JavaScript.
So here's the code:
var request = indexeddb.open(bla, version);
request.onsuccess = function (event) { };
So the open-method returns a IDBOpenDBRequest object, which, according to Mozillas site, inherits from IDBRequest, which apart from properties and methods also has event handlers, one of them being onsuccess:
https://developer.mozilla.org/en-US/docs/Web/API/IDBRequest.onsuccess
So on the mozilla site, onsuccess is just function () { }
Now, when the database was opened sucessfully, the "onsuccess" event fires and the appropiate event handler is called, in this case the function that I defined. But how exactly does that happen?
The request variable contains an instance of the IDBOpenDBRequest. So when I write request.onsuccess = somefunction(), am I overwriting the default function of the IDBRequest-class?
I dont get why I can write request.onsuccess = somefunction(event) { } and how the event is passed to that function.
EDIT:
function myObect() {
this.open = function(a,b,c) {
if (c > 20) {
this.success("String");
}
};
};
var myrequest = new myObect();
myrequest.open(4,2,21);
myrequest.success = function (ev) {
console.log(ev);
};
To create a similar api, you can do something like:
function open(a, b, c) {
var request = {};
if(c > 20) {
setTimeout(function() {
if(typeof request.success === "function") {
request.success("String");
}
}, 1);
}
return request;
}
var myrequest = open(4, 2, 21);
myrequest.success = function(ev) {
console.log(ev);
};
Here, setTimeout is asynchronous so the callback function is not executed immediately. When any asynchronous task is run in JavaScript, the currently executing code will run to completion before any callback is called. So success is guaranteed to be set before request.success called.
The Indexed DB open call similarly runs an asynchronous task, and then dispatches events when it is finished which will eventually call your callback function.
I overwriting the default function of the IDBRequest-class
Looks like there is no default behavior, so you just set up your own func.

Categories

Resources