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);
};
Related
Hi I'm trying to understand callbacks in javascript and have come across this code here from a tutorial that I'm following:
var EventEmitter = require('events');
var util = require('util');
function Greetr() {
this.greeting = 'Hello world!';
}
util.inherits(Greetr, EventEmitter);
Greetr.prototype.greet = function(data) {
console.log(this.greeting + ': ' + data);
this.emit('greet', data);
}
var greeter1 = new Greetr();
greeter1.on('greet', function(data) {
console.log('Someone greeted!: ' + data);
});
greeter1.greet('Tony');
Now I notice that the greeter1.on function takes a callback with a parameter. However I'm not sure how this is implemented internally. I tried looking through the nodejs event.js file but I'm still confused. I am aware that there are ways around this specific implementation by using an anonymous function wrapping the callback with parameters but I want to understand how to use the same format as above.
tldr: How can I create my own function that takes a callback and a parameter in the same fashion as greeter1.on above.
Thank you
Your function needs to define a new property on the current instance with the callback passed as an argument, so it can be called later, like so:
function YourClass () {
this.on = function(key, callback) {
this[key] = callback;
}
}
// Usage
const instance = new YourClass();
instance.on('eventName', function (arg1, arg2) {
console.log(arg1, arg2);
});
instance.eventName("First argument", "and Second argument")
// logs => First argument and Second argument
Callback is just passing a function as a parameter to another function and that being triggered. You can implement callback fashion as below
function test(message, callback) {
console.log(message);
callback();
}
//Pass function as parameter to another function which will trigger it at the end
test("Hello world", function () {
console.log("Sucessfully triggered callback")
})
class MyOwnEventHandler {
constructor() {
this.events = {};
}
emit(evt, ...params) {
if (!this.events[evt]) {
return;
}
for (let i = 0, l = this.events[evt].length; i < l; i++) {
if (!params) {
this.events[evt][i]();
continue;
}
this.events[evt][i](...params);
}
}
on(evt, eventFunc) {
if (!this.events[evt]) {
this.events[evt] = [];
}
this.events[evt].push(eventFunc);
}
}
var myHandler = new MyOwnEventHandler();
myHandler.on('test', function (...params) {
console.log(...params);
});
myHandler.emit('test', 'Hello', 'World');
The system I'm working with was designed to only make synchronous ajax calls, so i am looking for a workaround. First i have an ajax call that is wrapped in a function. I then wrap it in another function so it doesn't get executed when adding it to the array. So i have two arrays of async ajax call functions. I would like to execute everything in the first array, and then wait until everything has completed. I would then like to execute everything in a second array. This is what i have so far
I have a loop that goes through items and I have a wrap function for each item that takes in my already wrapped ajax call so that it doesn't get executed and stores it in an array like below
var postpromises = [];
var WrapFunction = function (fn, context, params) {
return function () {
fn.apply(context, params);
};
}
var postPromise = WrapFunction(ajaxFunction, this, [{
url: url,
data: j,
async: true,
type: 'POST',
success: function (data) {
//success
},
error: function (xhr, textStatus, errorThrown) {
//error
}
}]);
postpromises.push(postPromise);
I then have the same code for validation. So before I move on to next page, I have the following
$.when.apply(undefined, postpromises).then(function () {
console.log();
$.when.apply(undefined, validatepromises).then(function () {
console.log();
});
});
The issue is that when I get to the code above, none of my postpromises even get executed, so I feel like I may be missing something here.
Ideas?
The function $.when require a promise, in your code you are returning a function that return nothing, so just return the result of the wrapped function:
ES6 spread operator REF
function arguments object REF
var postpromises = [];
var validatepromises = [];
function f1() {
var fakePromise = $.Deferred();
setTimeout(() => {
fakePromise.resolve("IM RESOLVED!!");
}, 500);
return fakePromise.promise();
}
//OLD ONE
/*var WrapFunction = function (fn, context, params) {
return function () {
fn.apply(context, params);
};
}*/
var WrapFunction = function(fn, context, params) {
return function() {
return fn.apply(context, params);
}();
}
var postPromise = WrapFunction(f1, this, []);
postpromises = [postPromise];
var validatePromise = WrapFunction(f1, this, []);
validatepromises = [validatePromise];
//OLD ONE
/*$.when.apply(undefined, postpromises).then(function(res) {
console.log(res);
$.when.apply(undefined, validatepromises).then(function(res) {
console.log(res);
});
});*/
$.when.apply(null, [...postpromises, ...validatepromises]).then(function() {
console.log([].slice.call(arguments))
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I have a section in my code that looks like this
var locationDefer = $.Deferred();
if (saSel.Company === -1) {
database.getAllLocations().then(function (result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
});
} else {
database.getLocationsForCompany(saSel.Company).then(function (result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
});
}
However, since it is basically the same thing twice, just with a different ajax call - is there any way to either have the anonymous function part
function (result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
})
declared as a real function and then just called in the .then() clause, or can I somehow provide the to-be-called-function of the database object?
For the latter, I had something in my mind that could look like this, but I have no clue how to do the last line.
if(saSel.Company === -1) {
fun = 'getAllLocations';
arg = null;
} else {
fun = 'getLocationsForCompany';
arg = saSel.Company;
}
// database.fun(arg).then(function (result) {...});
You can define a function and pass its reference as success callback handler
//Define the function handler
function resultHandler(result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
}
if (saSel.Company === -1) {
fun = 'getAllLocations';
arg = null;
} else {
fun = 'getLocationsForCompany';
arg = saSel.Company;
}
//Invoke the method using Bracket notation
//And, pass the success handler as reference
database[fun](arg).then(resultHandler);
Additionally, as getLocationsForCompany() and getAllLocations() returns a promise, you shouldn't use $.Deferred() directly return Promise
return database[fun](arg);
I have a function that makes a GET request for a nested JSON object. The object is returned successfully but you can't access the other objects within the returned object.
the object looks like this :
{
"student": {
"hobbies": ["reading", "dancing", "music"],
"subjects": ["english", "maths", "science"]
}
}
and this is the function :
var superObject = {
getData: function(obj) {
$.get(obj.target, function(callbackObject) {
// It works fine if i log callbackObject
// console.log(callbackObject);
return callbackObject;
}
},
useData: function() {
var data = superObject.getData({'target': 'file.json'});
var hobbies = data.student.hobbies;
console.log(hobbies); // This fails and returns nothing.
}
}
Due to asynchronous Ajax behaviour, you need to pass a callback function to execute once the data retrieved via Ajax is available; something like:
getData: function(obj, callback) {
$.get(obj.target, function(callbackObject) {
callback.call(null, callbackObject);
}
}
useData: function() {
superObject.getData({'target': 'file.json'}, function(data) {
var hobbies = data.student.hobbies;
});
}
The problem is that you're returning callbackObject from your $.get callback, and not from your getData function. $.get is asynchronous, so its callback will not fire until long after getData() has finished. That's why you're seeing undefined.
What about something like:
var superObject = {
getReuslts: {},
getData: function(obj) {
$.get(obj.target, function(callbackObject) {
getReuslts = callbackObject;
this.useData();
}
},
useData: function() {
var hobbies = getReuslts.student.hobbies;
console.log(hobbies);
}
}
Of course this would create a temporal dependency between useData and getData. Why not create this object in a function so you can add some encapsulation?
funcition getSuperObject = {
var result = {};
var getReuslts = {};
function useData() {
var hobbies = getReuslts.student.hobbies;
console.log(hobbies);
}
result.getData = function(obj) {
$.get(obj.target, function(callbackObject) {
getReuslts = callbackObject;
useData();
});
};
return result;
}
Or supply your own callback:
var superObject = {
getData: function(obj, callback) {
$.get(obj.target, function(callbackObject) {
if (callback)
callback(calbackObject);
});
}
}
And then
superObject.getData({'target': 'file.json'}, function(result) {
var hobbies = result.student.hobbies;
console.log(hobbies); // This fails and returns nothing.
});
$.get works asynchronously: you call it, then the browser goes off to make the request. Meanwhile, your code continues running. When the browser gets the response from the server, it invokes the callback function you provided passing it the results.
This means that when getData runs, it will return "almost immediately" and the request started by $.get will still be in progress in the background. So, getData cannot return anything meaningful to its caller because it can only schedule a requestl; it cannot know what the result will end up being.
So it follows that you cannot call getData like this:
var data = superObject.getData({'target': 'file.json'});
What you need to do instead is put the code that depends on the response inside the callback:
$.get(obj.target, function(data) {
var hobbies = data.student.hobbies;
console.log(hobbies); // This will now work
}
The scope of your variables is only in your function
You can try to set the variable hobbies out of your structure, and set it's value inside of your function
var hobbies; //global scope
var superObject = {
getData: function(obj) {
$.get(obj.target, function(callbackObject) {
// It works fine if i log callbackObject
// console.log(callbackObject);
return callbackObject;
}},
useData: function() {
var data = superObject.getData({'target': 'file.json'});
hobbies= data.student.hobbies;
//set it's value
}
}
You are returning in the callback handler. So your result is sent to the callback dispatcher which drops your result. What you need to do is attach your processing code to the get callback:
getData: function(obj) { return $.getJSON(obj.target); },
useData: function() {
getData({ target : 'file.json'}).then(function(results) {
var hobbies = results.student.hobbies;
console.log(hobbies);
}
}
You could turn your asynchronous AJAX call to be synchronous. See http://api.jquery.com/jQuery.ajax/, mainly the "async" option.
See this discussion:
Ajax jquery synchronous callback success
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