JavaScript/jQuery context - proxy? - javascript

I have to animate an element in an sync way (that is to wait until the animation (fadeIn() in this example) is completed, so I can propertly continue).
As fas as I know I have three options:
doSomething: function () {
$('.someElement').fadeIn(function () {
// do something else here...
});
}
Another approach (using $.proxy() to alter the context):
doSomething: function () {
$('.someElement').fadeIn($.proxy(function () {
this.doSomethingElse();
}, this));
},
doSomethingElse: function () {
// ...
}
However, since using $.proxy() kinda "mixes" the contexts and I really don't like this (because I just don't know when it'll come back and bite my ass off), I'm sometimes using this approach:
doSomething: function () {
$('.someElement').fadeIn(function () {
doSomethingElse();
});
function doSomethingElse () {
// ...
}
}
Now I'm wondering, is there any better approach? Something simpler and more elegant. Like:
doSomething: function () {
$('.someElement').fadeIn(function () {
// do this in an async way...
});
// simply do something else here...
}
This problem really bugs me, because I like clean code and I don't like this simple, yet un-clean solutions.

Create a mediatory variable to refer to your original object:
doSomething: function () {
var that = this;
$('.someElement').fadeIn(function() {
// use "that" here
});
}

If doSomethingElse() is another animation, you can chain and queue them mantaining the original context.

Related

javascript bind callback to any function

I would like to bind or chain a callback/function to another function.
There is a similar question (without any valid answer): jQuery , bind a callback to any function
But, for me I would not want to be limited only to jQuery realm.
I looking for a broader answer, I wonder if it's possible with vanilla javascript or library other than jQuery.
Example:
// a function which I don't want or I can't edit it's body
var thirdPartyObject = {
doSomething: function(args) {
// a lot of code
console.log('doing stuff: ' + args);
}
};
// my function
var easyCallback = function() {
// processing stuff
console.log('doing more stuff');
}
// the bind
magicLibrary.bind(thirdPartyObject.doSomething, easyCallback);
// run
thirdPartyObject.doSomething(1);
thirdPartyObject.doSomething(10);
When I run this "code", the following output represents the behaviour I'm looking for:
doing stuff: 1
doing more stuff
doing stuff: 10
doing more stuff
Is it possible?
EDIT: the bind is a conceptual term, maybe you think this like a chain, trigger or even another term.
But the import is the second function or callback which in my example is easyCallback() must be somehow connected to the first one doSomething().
And every time the doSomething() is called or executed I want the easyCallback() to be executed as well after the first is finished.
But the without "wrapping" them around or without rewriting the first one.
You would have to wrap the doSomething function inside yet another function, like so:
// a function which I don't want or I can't edit it's body
var thirdPartyObject = {
doSomething: function(args) {
// a lot of code
console.log('doing stuff: ' + args);
}
};
// my function
var easyCallback = function() {
// processing stuff
console.log('doing more stuff');
}
// the bind
// magicLibrary.bind(thirdPartyObject.doSomething, easyCallback);
const doSomething = thirdPartyObject.doSomething.bind(thirdPartyObject);
thirdPartyObject.doSomething = function(args) {
doSomething(args);
easyCallback(args);
};
// run
thirdPartyObject.doSomething(1);
thirdPartyObject.doSomething(10);

Nesting callbacks for more than two tasks

It is possible to determine the order of TWO tasks using callbacks, as shown below.
a(b);
function a(callback) {
// do something
callback();
}
function b() {
// do next
}
See Fiddle
First do a(), then do b().
I would like to concatenate more than two tasks.
As I´m dealing with quite big functions, I´m looking for something like that:
a(b(c));
First do a(), then do b(), then do c().
However I'm not successful with this. See Fiddle
Is there an easy way to do so, maybe without needing Promises?
You're calling b immediately, not passing a callback to a. You'll need to use a function expression:
a(function(aResult) {
b(c);
});
Of course, you can avoid these by returning closures from all your functions:
function a(callback) {
return function(args) {
// do something
if (callback) callback(res);
};
}
function b(callback) {
return function(aResult) {
// do next
if (callback) callback(res);
};
}
function c(callback) {
return function(bResult) {
// do next
if (callback) callback(res);
};
}
which you would call like this:
a(b(c())();
(this is known as pure continuation passing style)

returning array from function in javascript

so I come from a heavy python background, and I'm trying to wrap my head around javascript. Here I have a function that returns an array of track IDs for soundcloud songs by the artist 'v-2-followers'. How would I go about assigning the output of SC.get(stuff) to a variable to reuse the track list in another function. I'm sure I'm missing something fundamental. I'm less looking for an answer that explains how to do this, but more why it's done like that.
That said I would also very much appreciate the how. :)
(function() {
SC.initialize({
client_id:'__CLIENTID__';
});
// Would like to set a variable equal to the output of
SC.get('/tracks', { q: 'v-2-followers' }, function(tracks) {
trackIdList = [];
tracks.forEach(function(track){
trackIdList.push(track.id);
});
return trackIdList;
});
// And use the variable here.
SC.stream('/tracks/'+trackIdList[Math.floor(Math.random() * myArray.length)], function(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function(e){
sound.resume();
}, function(e){
sound.pause();
});
});
})();
I can see that I'm missing something fundamental about variable assignment and scope, or function callbacks here. I've exhausted myself skimming docs on the subject. If anyone can tell me how to do this, and more importantly, why it's done that way, for future reference.
You have trackIdList as a global variable because it is not created using var. So as it is, you can already access it from any other function. If you wanted to limit its scope to just the outer function, add var trackIdList; as the first line of your function. You should be declaring variables with var everywhere in order to limit their scope.
(function() {
var trackIdList;
...
})();
Further reading: What is the scope of variables in JavaScript?
The other concept you need to understand is regarding asynchronous execution and callbacks in JavaScript. Your code that populates trackIdList is contained within a callback function which is (most likely) called after your call to SC.stream(). If SC.stream() depends on the value of trackIdList, it should be called from the callback function.
It may help to illustrate what's going on by separating out your callback functions.
(function () {
var trackIdList = [];
SC.initialize({
client_id: '__CLIENTID__'
});
SC.get('/tracks', { q: 'v-2-followers' }, processTracks);
var randomIndex = Math.floor(Math.random() * myArray.length);
SC.stream('/tracks/' + trackIdList[randomIndex], processSound);
function processTracks(tracks) {
tracks.forEach(function (track) {
trackIdList.push(track.id);
});
}
function processSound(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function (e) {
sound.resume();
}, function (e) {
sound.pause();
});
}
})();
SC.get() makes an asynchronous request and returns immediately. Then SC.stream() is called without waiting for the request to return. processTracks() isn't called until the request comes back. The trouble is that SC.stream() depends on processTracks(), but is called immediately. To fix this, call SC.stream() from the callback function of SC.get():
(function () {
SC.initialize({
client_id: '__CLIENTID__'
});
SC.get('/tracks', { q: 'v-2-followers' }, processTracks);
function processTracks(tracks) {
var trackIdList = [];
tracks.forEach(function (track) {
trackIdList.push(track.id);
});
var randomIndex = Math.floor(Math.random() * myArray.length);
SC.stream('/tracks/' + trackIdList[randomIndex], processSound);
}
function processSound(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function (e) {
sound.resume();
}, function (e) {
sound.pause();
});
}
})();
I'll explain one way - with callbacks. The reason people do it this way is that there are synchronous operations, and asynchronous operations. In your case, you need to perform an AJAX request - we don't know how long it will take for SC.get to finish, and we don't want the program to hang while we wait for it. So instead of waiting, we tell it "go get those tracks, and I'm passing you a function to call when you are done. In the meantime, I'm going to go on ahead with the rest of the program."
(function() {
SC.initialize({
client_id:'__CLIENTID__'
});
var getTracks = function(callback) {
SC.get('/tracks', { q: 'v-2-followers' }, function(tracks) {
trackIdList = [];
tracks.forEach(function(track){
trackIdList.push(track.id);
});
callback(trackIdList);
});
}
// And use the variable here.
var stream = function(trackIdList) {
SC.stream('/tracks/'+trackIdList[Math.floor(Math.random() * myArray.length)], function(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function(e){
sound.resume();
}, function(e){
sound.pause();
});
});
}
getTracks(stream);
})();

Delay Between Functions

this is my code snippet.
function customFadeIn () {
$("img.imgKit").each(function(index) {
$(this).delay(1000*index).fadeIn("slow");
});
console.log("one runs");
}
function customFadeOut () {
$("img.imgKit").each(function(index) {
$(this).delay(1000*index).not(document.getElementById('card-6')).fadeOut("slow" , function () {
$("#card-6").delay(1000).rotate({angle:0});
});
});
console.log("two runs");
}
I want the customFadeOut runs only after customFadeIn is done, therefore I call it by this
customFadeIn();
customFadeOut();
But it did not work, I think I did something wrong here, a help would be really helpful.
You can make usage of jQuerys Deferred / promise objects. Animations do also "inherit" those objects and you can apply jQuery.when() to shoot for multiple promises to finish.
There are several ways to re-structure your code for that, a simple implementation of this could look like:
(function() {
var promises = [ ];
function customFadeIn () {
$("img.imgKit").each(function(index) {
promises.push( $(this).delay(1000*index).fadeIn("slow").promise() );
});
}
function customFadeOut () {
jQuery.when.apply( null, promises ).done(function() {
$("img.imgKit").each(function(index) {
$(this).delay(1000*index).not(document.getElementById('card-6')).fadeOut("slow" , function () {
$("#card-6").delay(1000).rotate({angle:0});
});
});
console.log("two runs");
});
}
}());
If I did everything correct there, customFadeOut sets up a listener which waits for all animations / promises to finish, before it runs its own code. You don't even have to explicitly call the .promise() method at the end, jQuery applies some white magic to link that node with a promise internally for you.
Demo: http://jsfiddle.net/RGgr3/
Looks like I did everything correct ;)

What's the jQuery equivalent to this, and what does it do?

I'm converting some javascript to jQuery from ExtJS and I don't know what this does so I'm not sure what it converts to...
hideTimeout = setTimeout(this.hideAll.createDelegate(this), delay);
delay = 200
What I'm not sure about is the createDelegate(this)...
update
All the JS is...
Menu.prototype = {
init: function () {
var that = this;
this.ui.link.bind("mouseover", function (e) {
that.show();
});
this.ui.link.bind("mouseout", function (e) {
that.hide();
});
var subOptions = $("li", this.ui.parent);
$.each(subOptions, function (el) {
el = $(el);
el.bind("mouseover", that.cancelTimeout, this);
el.bind("mouseout", that.hide, this);
});
},
hideAll: function () {
$("#hd .nav ul ul").hide();
},
show: function () {
this.hideAll();
this.cancelTimeout();
showTimeout = setTimeout((function () {
this.el.show();
}).createDelegate(this), delay);
},
hide: function () {
this.cancelTimeout();
hideTimeout = setTimeout(this.hideAll.createDelegate(this), delay);
},
cancelTimeout: function () {
clearTimeout(hideTimeout);
clearTimeout(showTimeout);
}
};
Because you're in a setTimeout, this will represent the window object.
I don't know ExtJS, but it appears to be creating a delegate handler on the window.
Probably best to reference the ExtJS docs. According to the docs for createDelegate:
Creates a delegate (callback) that sets the scope to obj. Call directly on any function. Example: this.myFunction.cre...
EDIT: I believe it would be called like this:
hideTimeout = setTimeout($.proxy( this.hideAll, this), delay);
It will ensure that when hideAll is called, it will be called in its current context.
You can do the same thing for the anonymous function passed to setTimeout in show:
showTimeout = setTimeout($.proxy(function () {
this.el.show();
}, this), delay);
You can accomplish the same thing with jQuery like so:
hideTimeout = setTimeout(jQuery.proxy(this, "hideAll"), delay);
EDIT: Since the method hideAll doesn't contain a reference to this, you can accomplish this even more simply:
hideTimeout = setTimeout(this.hideAll, delay);

Categories

Resources