Usually, I declare a function with success and fail callbacks as follow
function yoyoyo(param, successCallback, failCallback) {
// do something with param
// ...
if (success) {
successCallback('success');
} else {
failCallback('fail');
}
}
then I will use it like this
yoyoyo('abc', function(success) {
console.log(success);
}, function(err) {
console.log(err);
});
BUT, when I look into Parse Javascript Guide, they guide me to use the function like this (i.e. merge success and fail callbacks in one object?)
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.get("xWMyZ4YEGZ", {
success: function(gameScore) {
// The object was retrieved successfully.
},
error: function(object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
How can I declare my function with success and fail callbacks like parse does?
You would just change your function to accept a callbacks arg (call it whatever you want) and then access the handlers off that object:
function yoyoyo(param, callbacks) {
// do something with param
// ...
if (success) {
callbacks.success('success');
} else {
callbacks.error('fail');
}
}
then you would call it with:
yoyoyo('abc', {
success: function(status) {
},
error: function(status) {
}
});
Note though, that your code should check to ensure that the object passed in has both of the methods before attempting to call them.
That method is accepting a object that contains two function pointers. So it's a bit like creating an object thus:
var parameters = {success:function() {}, error:function(){}};
You can simply change your method thus:
function yoyoyo(param, callbacks) {
//Add some error checking to check the callbacks is in the right state
if (typeof callbacks.success != "undefined" && typeof callbacks.error!= "undefined")
{
// do something with param
// ...
if (success) {
callbacks.success('success');
} else {
callbacks.error('fail');
}
}
else {
throw "callbacks must contain a success and error method";
}
}
then call it:
.yoyoto(param, {success:function() {}, error:function(){}});
Looks like what you want is an argument that is an object with success and error functions as attributes
You can declare the function like so
function (param, callbacks) {
// do something with param
// ...
if (success) {
if(callbacks && callbacks.success) callbacks.success('success');
} else {
if(callbacks && callbacks.fail) callbacks.fail('fail');
}
}
As a side note since I see you've tagged node.js, I would also recommend you look at using a single error-first callback, which keeps your functions/apis simple and also follows a rather standard node convention.
Related
I have a jQuery ajax function like this:
jQuery.ajax({
url : '/blabla',
method : 'post',
data: {
bla : bla
}
}).done(function(data) {
// do lots of stuff
});
.. and I want to be able to add a check that the data passed into the done callback function doesn't have a session_timed_out value in it. Say I have many functions similar to the one above but they all do different things, but they ALL need to check if the session timed out first. Is there a proper way to extend done() so it initially checks for a timeout? I tried to do something like this but it failed:
var myAjax = function(options,callback){
var defaults = {
done: function(data){ //hijack the success handler?
if(check(data)){
callback(data);
}
}
};
jQuery.extend(options,defaults);
return jQuery.ajax(options);
}
When I use this extended function it works like before, meaning the check never gets called because it seems to be superseded by the done() callback in the actual implementation, which I guess makes sense. So I want to know if there is a way to "decorate" or extend done() function so it initially checks for the session timeout first. Or will I need to manually add this same session check to all of my ajax done's?
This snippet overrides the jQuery ajax method so you can add an extra check when it successfully returns.
(function($) {
var yourCustomCheck = function(ajaxRes) {
// Do whatever you need and return a boolean
};
var oldAjax = $.ajax;
$.ajax = function(opts) {
return $.Deferred(function() {
var _def = this;
oldAjax.call(this, opts).done(function(res) {
console.log("this is done first");
if(yourCustomCheck.call(this, res)) _def.resolve(res);
else _def.reject("timeout");
}).fail(function() {
_def.reject();
});
})
}
})(jQuery);
After this, you can use $.ajax() normally..
$.ajax({
.....
}).done(function(res) {
console.log("ok");
}).fail(function() {
console.log("no ok");
});
Here is a jsfiddle with a working example: https://jsfiddle.net/jormaechea/kffyo7qL/1/
You could chain a timeout checker:
jQuery.ajax({
url : '/blabla',
method : 'post',
data: {
bla : bla
}
}).then(timeoutCheck).then(function(data) {
// do lots of stuff
}, function(err) {
// handle error
});
function timeoutCheck(data) {
if (check(data)) {
return data;
} else {
// return a rejected promise to turn fulfilled into reject
return jQuery.Deferred.reject(new Error("timeout"));
}
}
Or, you could put this in your own ajax wrapper.
jQuery.ajaxT = function() {
return jQuery.ajax.apply(jQuery, arguments).then(timeoutCheck);
}
jQuery.ajaxT(...).then(function(results) {
// handle returned data here
// the timeoutCheck has already been done
}, function(err) {
// handle any errors here
});
Then, any ajax call you initiated with jQuery.ajaxT() would automatically have the timeoutCheck added to it's promise logic. If the ajax call succeeds and the timeout check passes, then the promise is fulfilled. If the ajax call succeeds and the timeout check fails, then the promise rejected.
I'm trying to write a small XHR abstraction as well as learn how to create chainable methods, I am nearly there (I think), but am at a loss as to what to do next, I think my setup is wrong.
What I want to do:
$http.get('file.txt')
.success(function () {
console.log('Success');
})
.error(function () {
console.log('Error');
});
What I've got:
window.$http = {};
$http.get = function (url, cb, data) {
var xhr = {
success: function (callback) {
callback();
return this;
},
error: function (callback) {
callback();
return this;
}
};
// just a test to call the success message
if (window) {
xhr.success.call(xhr);
}
return xhr;
};
I'm having trouble 'wiring' up the success/error messages, can anybody help point me in the right direction? Thanks in advance.
jsFiddle
Your chaining is OK, but you have a error at this line:
if (window) {
xhr.success.call(xhr); // Uncaught TypeError: undefined is not a function
}
So JavaScript breaks and doesn't return xhr. Delete thoses lines and it will work.
success and error are simply functions that store the passed functions into an internal storage. Once the XHR responds, your code should execute all callbacks accordingly, depending on the response status.
Now what you need is an object instance per request that stores its own set of success and error callbacks. Also, success and error methods should return the same instance to allow chaining.
This should set you to the right track:
(function (window) {
window.$http = {};
// An object constructor for your XHR object
function XHRObject(url,data){
// store the request data
this.url = url;
this.data = data;
// The callback storage
this.callbacks = {};
this.init();
}
// Methods
XHRObject.prototype = {
init : function(){
// Actual call here
// Depending on result, execute callbacks
var callbacksToExecute;
if(readyState === 4 && response.status === 200){
callbacksToExecute = this.callbacks.success;
} else {
callbacksToExecute = this.callbacks.error;
}
callbacksToExecute.forEach(function(callback){
callback.call(null);
});
},
success : function(cb){
// Create a success callback array and store the callback
if(this.callbacks.hasOwnProperty('success') this.callbacks.success = [];
this.callbacks.success.push(cb);
// You also need a flag to tell future callbacks to execute immediately
// if the current object has already responded
return this;
},
...
}
// A call to get basically returns an object
$http.get = function (url, data) {
return new XHRObject(url,data);
};
})(this);
I hope you can make something out of this:
window.$http = {};
$http.get = function (url, cb, data) {
var xhr = function(){
return {
success: function (callback) {
callback();
return this;
},
error: function (callback) {
callback();
return this;
}
};
};
return new xhr();
}
$http.get('url','cb','data')
.success(function () {
console.log('Success');
})
.error(function () {
console.log('Error');
});
Edit: I just realized this is basically the same code you wrote, except I'm missing the if(). It seems that test was causing the code to break.
I am currently linking a javascript file to an html page, and upon using a function in that javascript file the return value essentially gets erased and shows up as undefined, even though in the function itself the value is defined (that was probably very confusing, i'll just show the code and it should make sense):
functions.js
function addActivity(contactNameSelected, username) {
var returnArray = [];
//post to .php
if(data.added)
{
var newEvent = [];
newEvent['id'] = data.id;
newEvent['date'] = formattedDate;
returnArray.push(true);
returnArray.push(newEvent);
return returnArray; //when i debug, this has a value and is a valid array at this point
}
else
{
returnArray.push(false);
returnArray.push(data.message); //when i debug, this has a value and is a valid array at this point
return returnArray;
}
}
home.html
var response = [];
response = addActivity(contactNameSelected, username); //although valid above, undefined here
if(response[0]) //error b/c response is undefined
{
//do stuff if successful
}
else{
//do other stuff if unsuccessful
}
If i just return a string it works fine, but for some reason if i attempt to return an array it is simply undefined. Why is this?
Thanks!
I'm guessing that the omitted '//post to .php' looks something like
$.post('...php', { ... }, function(data) {
if (data.added) ...
The AJAX response is handled by a callback function, which executes asynchronously. In other words, returnArray is populated well after addActivity has returned.
The return returnArray; statements are useless because you are returning a value from the callback, not from addActivity. The callback is invoked not by your code, but by XHR (in a different execution context) and its return value is discarded.
To properly pass your data back in asynchronous style, we need to tweak your code.
function addActivity(contactNameSelected, username, callback) {
$.post('...', { ... }, function(data) {
var returnArray=[];
if(data.added)
{
...
}
else
{
...
}
callback(returnArray);
});
}
addActivity(contactNameSelected, username, function(response) {
if(response[0])
{
...
}
else
{
...
}
});
I am writing a javascript function that takes a callback. The callback will be passed an error argument if something goes wrong.
What are the best / most standard calling conventions?
Should the callback's error argument be first or last?
Should I pass an 'errorMsg' string, or a new Error('errorMsg') object?
Ie, what is more correct - this code:
foo = function(bar, callback) {
...
if (error) {
callback('troz not found');
} else {
callback(null, result);
}
}
or this code:
foo = function(bar, callback) {
...
if (error) {
callback(null, 'troz not found');
} else {
callback(result);
}
}
or this:
foo = function(bar, callback) {
...
if (error) {
callback(null, new Error('troz not found'));
} else {
callback(result);
}
}
If its relevant, my code will be used as both as a NodeJS module and as a browser-based javascript library.
You could specify two callbacks, one for success and one for error, and both encapsulated in a single "callbacks" argument. This is how many Javascript libraries handle your requirement.
var fooFn = function(bar, callbacks) {
callbacks = callbacks || {}; //make callbacks argument optional
...
if (error) {
if (callbacks.error) {
callbacks.error('troz not found'); //pass the error message as a string
}
} else if (callbacks.success) {
callbacks.success(result);
}
}
The success and error functions are optional. To specify both, call it like this:
fooFn("some bar", {
success: function(result) {
//success :-)
},
error: function(errorMsg) {
//error :-(
}
});
You can also do this:
fooFn("some other bar");
If you like, you can expand the callbacks object to support other scenarios, like "complete".
Most node.js apps either use the 'error-first' pattern or the EventEmitter pattern. viz:
// more common
if (error) {
cb({make:'better'})
} else {
cb(undefined, stronger)
}
or
// cleaner, perhaps, but more keystrokes
var ee = new EventEmitter
process.nextTick(function () {
...
if (error) {
ee.emit('error', {msg:'delicious'})
} else {
ee.emit('success', bacon)
}
})
return ee
You should choose a convention for your library and stick with it; otherwise, it is arbitrary. There are many different ways of handling errors in JavaScript:
Taking both a "success" and "error" callback.
Taking a single callback to handle both "success" and "error" cases.
Having a registration mechanism for a default "error" handler, making error handlers optional on all other calls, using the immediate or fallback error callbacks as appropriate.
Combined with:
Using no parameters to the error callback.
Using a boolean to indicate success / failure.
Using a string to encapsulate the nature of the error.
Using some more complex "Result" object to encapsulate success/failure and the result.
Using some complex object to encapsulate detailed failure information.
Really, it's entirely up to you. It also depends on what you are trying to do. For example, if you don't really intend to do anything with the failure information, then it might not be worth the expense of constructing / passing around that extra information. But perhaps you have lots of detailed failure information you want to pass to the user or back to the API caller, in which case it makes lots of sense.
Choose a convention, and stick with it. Other than that, it's entirely up to you.
Seems we have lack of modern response here :)
Now you can use Promise for it:
foo = function (bar) {
return new Promise(function (resolve, reject) {
...
if (error) {
reject('troz not found');
} else {
resolve(result);
}
});
}
or simpler, if action is synchronous:
foo = function (bar) {
...
if (error) {
return Promise.reject('troz not found');
} else {
return Promise.resolve(result);
}
}
and then, to handle result:
foo.then(callback)
.catch(errorHandler);
where
callback = function (result) {...}
errorHandler = function (errorMessage) {...}
so that, in case there was no error callback(result) will be done, in case of error errorHandler('troz not found') will be done.
As additional benefit - having result handler and error handler separately. Nice example of separation of concerns.
We have this anonymous function in our code, which is part of the jQuery's Ajax object parameters and which uses some variables from the function it is called from.
this.invoke = function(method, data, callback, error, bare) {
$.ajax({
success: function(res) {
if (!callback) return;
var result = "";
if (res != null && res.length != 0)
var result = JSON2.parse(res);
if (bare)
{ callback(result); return; }
for (var property in result) {
callback(result[property]);
break;
}
}
});
}
I have omitted the extra code, but you get the idea. The code works perfectly fine, but it leaks 4 Kbs on each call in IE, so I want to refactor it to turn the anonymous function into a named one, like this.onSuccess = function(res) { .. }.
The problem is that this function uses variables from this.invoke(..), so I cannot just take it outside of its body. How do I correctly refactor this code, so that it does not use anonymous functions and parent function variables?
Update. I am thinking of creating a separate object, initializing it with the same parameters, and pass its onSuccess function as a parameter for jQuery's Ajax object. Although I suspect that it will still leak memory.
Update 2. I have found a few links suggesting that the actual leak might be caused by jQuery.
Simple jQuery Ajax call leaks memory in Internet Explorer
Memory leak involving jQuery Ajax requests
Still it was good to find a way to refactor this.
Update 3. I will wait for a more generic solution, before accepting an answer.
You can add extra params to the ajax request that can be accessed in the success callback:
this.invoke = function(method, data, callback, error, bare) {
$.ajax({
success: onSuccess,
invokedata: {
callback: callback,
bare: bare
}
});
};
var onSuccess = function(res) {
var callback = this.invokedata.callback,
bare = this.invokedata.bare;
if (!callback) return;
var result = "";
if (res != null && res.length != 0)
var result = JSON2.parse(res);
if (bare){
callback(result);
return;
}
for (var property in result) {
callback(result[property]);
break;
}
}
+1 for excellent, excellent question - I feel your pain - this is really nicely factored as it is.
One suggestion (and maybe this is what you meant by your update)...define a wrapper for onSuccess and make it return the function you want to assign. Then call the outer function and assign it to the "success" option, passing the values it needs. Those values will be pre-assigned to the variables in the inner function. Not actually sure if this will help - you still end up with an anonymous function - but worth a try
this.invoke = function(method, data, callback, error, bare) {
$.ajax({
success: onSuccess(callback, bare);
});
};
var onSuccess = function(callback, bare) {
return function() {
if (!callback) return;
var result = "";
if (res != null && res.length != 0)
var result = JSON2.parse(res);
if (bare)
{ callback(result); return; }
for (var property in result) {
callback(result[property]);
break;
}
}
}