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);
})();