I'm tryng to do some jQuery stuff when both instagramLoadError and imagesLoaded functions have been loaded.
For this I'm using if(instagramLoadError() && imagesLoaded()){}) but it's not working as expected.
I'm just starting with JS but I read something about "scope" saying that I can't target my instagramLoadError and imagesLoaded functions since it's not within the same scope. For this reason or another I'm still getting the "x function is not defined" error.
What should I do in this case?
$(document).ready(function(){
(function(){
new InstagramFeed({
'on_error': function instagramLoadError(){
return true;
}
});
})();
});
$(document).ready(function(){
$('#myDiv').imagesLoaded(function(){
return true;
});
});
$(document).ready(function(){
if(instagramLoadError() && imagesLoaded()){
// do some stuff if both instagramLoadError and imagesLoaded functions have been executed
}
});
Try like this:
$(document).ready(function() {
var isInstagramLoadError = false;
var isImagesLoaded = false;
(function() {
new InstagramFeed({
'on_error': function() {
isInstagramLoadError = true;
checkBoth();
}
});
})();
$('#myDiv').imagesLoaded(function() {
isImagesLoaded = true;
checkBoth();
});
function checkBoth() {
if (isInstagramLoadError && isImagesLoaded) {
// do some stuff if both instagramLoadError and imagesLoaded functions have been executed
};
};
});
The issue is that you are dealing with asynchronous callback functions. Each function will run when something happens (Instagram load error or images loaded) and the order of the two cannot be guaranteed. So to deal with this, have each function update a variable and then call a common function checkBoth() to check the variables.
you can use Promise.all method. which will wait for all promise to complete
$(document).ready(function(){
function Instagram(){
new InstagramFeed({
'on_error': function instagramLoadError(){
return true;
}
function images(){
$('#myDiv').imagesLoaded(function(){
return true;
}
Promise.all([Instagram(),images()]).then(res=>{
if(res[0] && res[1]){
// do some code
}
})
})
Promise. all will wait for functions to complete and store the data returning in an array.
Related
I am trying to understand how this code works. I finally figured out it is a loop. It is not a "while" or "for" loop, but it is a loop nonetheless by virtue of calling itself I think (please correct me if I am wrong).
I understand it's main function: to pass JQuery when it is loaded to my 'foo' function, when ever jQuery has loaded. To do that it checks for jQuery in Window and if not there it resets the timer(). That is the loop. I get that.
Let me explain what I do not understand:
the call: CheckDependency.Deferred.execute(foo);
why the "Deferred" keyword?
execute baffles me: I expect that if I call CheckDependency.Deferred.execute that it would only execute that method. Why does it obviously run the timer function. why could it not simply have that code after the timer() since it keeps looping there and then return jquery?
Speaking of return. Why is there a method in there? CheckDependency.Deferred.execute(foo); is as crazy to me as CheckDependency.Deferred.RETURN.execute(foo); (or some similar crazy statement)
I am fairly new to JavaScript (from PHP). Here the code:
function foo(){ console.log('jQuery found!');
}
var CheckDependency = CheckDependency || { };
CheckDependency.Deferred = function ()
{
var functions = [];
var timer = function() {
if (window.jQuery) {/* && window.jQuery.ui*/
while (functions.length) {
functions.shift()(window.jQuery);
}
} else {
window.setTimeout(timer, 250);
}
};
timer();
return {
execute: function(onJQueryReady)
{
if (window.jQuery) { // && window.jQuery.ui
onJQueryReady(window.jQuery);
} else {
functions.push(onJQueryReady);
}
}
};
}();
CheckDependency.Deferred.execute(foo);
Let me start by saying I'm not a javascript expert, but I dabble :) I'll take a stab at describing what is going on here.
First, This creates a new object called "CheckDependency".
var CheckDependency = CheckDependency || { };
Next, it runs an anonymous function, and stores the result in CheckDependency.Deferred.
CheckDependency.Deferred = function ()
{
.
.
.
.
}()
The anonymous function runs the following code:
var functions = [];
var timer = function() {
if (window.jQuery) {/* && window.jQuery.ui*/
while (functions.length) {
functions.shift()(window.jQuery);
}
} else {
window.setTimeout(timer, 250);
}
};
timer();
The last part of the function code returns a new function execute, which gives CheckDependency.Deferred a function execute.
return {
execute: function(onJQueryReady)
{
if (window.jQuery) { // && window.jQuery.ui
onJQueryReady(window.jQuery);
} else {
functions.push(onJQueryReady);
}
}
};
Finally, this new function is called
CheckDependency.Deferred.execute(foo);
The final result of this is that the code starts a background timer that calls itself until window.jQuery is true - which means jQuery is loaded. Then, the function passed to execute is passed into this loop and so will once jQuery is available, the original function passed to "execute" will be called with the instance of window.jQuery.
I hope I did this justice, and I hope my answer helps! Please let me know if you have any question.
What is the best way to fire a function once another has finished?
At the moment I have 6 functions that I'm calling on DOM load like this:
$(document).ready(function(){
func1();
func2();
func3();
func4();
func5();
func6();
});
function func1(){
do some stuff
}
function func2(){
do some stuff
}
function func3(){
do some stuff
}
function func4(){
do some stuff
}
function func5(){
do some stuff
}
function func6(){
do some stuff
}
But I want them to fire one after another. How would I do this?
Thanks in advance
Try by using callbacks like,
$(document).ready(function(){
func1(func2);
});
function func1(callback){
do some stuff
callback('func2');
}
function func2(callback){
do some stuff
callback('func3');
}
function func3(callback){
do some stuff
callback('func4');
}
function func4(callback){
do some stuff
callback('func5');
}
function func5(callback){
do some stuff
callback('func6');
}
function func6(callback){
do some stuff
}
Read callback-functions-javascript
You can refer Is there a better way to do callback chaining in javascript? for chained callbacks
function func1(){
do some stuff
_next();
}
function func2(){
do some stuff
_next();
}
function func3(){
do some stuff
_next();
}
function func4(){
do some stuff
_next();
}
function func5(){
do some stuff
_next();
}
function func6(){
do some stuff
_next();
}
function chainCallbacks() {
var _this = this;
var _counter = 0;
var _callbacks = arguments;
var _next = function() {
_counter++;
if(_counter < _callbacks.length) {
_callbacks[_counter].apply(_this);
}
};
_this._next = _next;
return function() {
if(_callbacks.length > 0) {
_callbacks[0].apply(_this);
}
};
}
var queue = chainCallbacks(func1, func2, func3,func4,func5,func6);
queue();
The way you wrote it is correct for synchronous code. However, based on your comment it sounds like you are firing off external requests that run asynchronously. What happens in this case is that function1 may fire off some code accessing Sharepoint and then will complete the rest of the code in function 1 after that...and then flow right into function2 without waiting to see what happens with the Sharepoint request.
My preferred way of dealing with this would be using callbacks from the asynchronous operations. Once you pull that data from Sharepoint, for instance, you are going to call some other function (we'll call it Sharepoint_callback1). Add a call to that function:
$(document).ready(function(){
func1();
});
function func1(){
do some stuff that calls Sharepoint, which after complete calls Sharepoint_callback1
}
function func2(){
do some stuff
}
function Sharepoint_callback1() {
do some stuff
func2();
}
There are other methods of doing this. JQuery provides callback handlers for almost of its operations, but it all essentially boils down to the same thing as above.
If I misunderstood the question I apologize; please post additional details and I'll modify my answer.
You could write a queue function. For queue of functions you can use array:
var fn1 = function() {},
fn2 = function() {},
MyQ = [];
//add functions to array
MyQ.push(fn1);
MyQ.push(fn2);
//remove and call the first item of an array
(MyQ.shift())();
If you want use your function with scope and arguments, you can wrap functions, for example:
MyFnWithParam = function(fn, scope, params){
scope = scope || window;
params = params || [];
return function() {fn.apply(scope, params)};
};
var fn1 = MyFnWithParam(fn, this, params),
fn2 = MyFnWithParam(fn, this, params),
MyQ = [];
MyQ.push(fn1);
MyQ.push(fn2);
while (MyQ.length > 0) {
(MyQ.shift())();
}
I need for a function to be executable only after an object is defined, I'm currently working in a fascade pattern and one method is dependent on another method. in this case 'addNewLayer' fails because 'setFullMap' hasn't finished executing. is there a solution? I'm using jquery and vanilla js so most any solution would be helpful at this point:
var jen = (function(){
function setFullMap(mapID){
jen.map = new Map(mapID);
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id);
addNewLayer(opt);
}
};
})();
Thanks
solution:
var jen = (function(){
function setFullMap(mapID, callback) {
jen.map = new Map(mapID);
if(jen.map){
callback();
}
}
return {
samp: function(id, opt){
setFullMap(id, function(){
addNewLayer(opt);
}.bind(this));
}
};
})();
You will have to pass a callback function to setFullMap, and execute it once the function has completed (at the very end, before the closing }).
var jen = (function(){
function setFullMap(mapID, callback){
jen.map = new Map(mapID);
callback();
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id, function() {
addNewLayer(opt);
}.bind(this));
}
};
})();
Do not forget using .bind(this) - it is very important in order to keep the original this in your callback function.
Edit:
Actually that would not work work if the Map constructor is a-synchronous. If you do not have access to the constructor and/or you cannot pass it a callback, then presumably the only (and sad) option would be to use a setTimeout or (easier) setInterval, continuously checking at defined intervals if the operation has been completed, and then fire the callback.
You could use a callback parameter:
function setFullmap(mapId,callback) {
jen.map = new Map(mapId);
callback();
}
....
samp: function(id, opt){
setFullMap(id,function() {
addNewLayer(opt);
});
}
When u dont have a way to manipulate the Map Object then u need to use a loop:
var loop=self.setInterval(function(){
if(jen.map) {
//execute code here after jen.map is defined
console.log(typeof jen.map);
window.clearInterval(loop);
}
},50);
Check jsfiddle:
http://jsfiddle.net/9yv5t/1/
I have checked the docs and it seems that there are various events you could listen to.
For example:
var m = new Map(...);
m.on('load', function () {
//execute code when the first layer is ready
});
var l = new Layer(...);
l.on('load', function () {
//execute code when the layer has been initialized
});
It's also carefully stated for the Layer.load event:
fires after layer properties for the layer are successfully populated.
This event must be successful before the layer can be added to the
map.
$("#div1, #div2").fadeIn('500',function(){
{
console.log('Test');
}
});
Fiddle here: http://jsfiddle.net/y97h9/
The above code will print 'Test' two times in the console. How can I make it print only one time. Is it possible?
Sure, you can use jQuery promise to solve multiple callbacks problem:
$("#div1, #div2").fadeIn('500').promise().done(function()
{
console.log('Test');
});
The .promise() method returns a dynamically generated Promise that is
resolved once all actions of a certain type bound to the collection,
queued or not, have ended
Working Demo
The callback will run once for every matched element. You can always set a flag to see if it's been run already though:
var hasRun = false;
$("#div1, #div2").fadeIn('500', function() {
if (hasRun) return;
console.log('Test');
hasRun = true;
});
Use a boolean flag to prevent console.log('Test'); from being called twice.
var isCalled = false;
$("#div1, #div2").fadeIn('500',function(){
if(!isCalled) {
isCalled = true;
console.log('Test');
}
});
I'm writing an engine that requires the use of getScript quite extensively. I've pushed it into its own function, for ease of use, but now I need to make sure that the function itself is synchronous. Unfortunately, I can't seem to make getScript wait until the script it loads is actually finished loading before proceeding. I've even tried setting jQuery's ajax asynch property to false before making the call. I'm thinking of using jQuery's when/done protocol, but I can't seem to wrap my head around the logic of placing it inside a function and making the function itself synchronous. Any help would be very much appreciated!
function loadScript(script){
//Unrelated stuff here!!!
$.when(
$.getScript(script,function(){
//Unrelated stuff here!!!
})).done(function(){
//Wait until done, then finish function
});
}
Loop code (by request):
for (var i in divlist){
switch($("#"+divlist[i]).css({"background-color"})){
case #FFF:
loadScript(scriptlist[0],divlist[i]);
break;
case #000:
loadScript(scriptlist[2],divlist[i]);
break;
case #333:
loadScript(scriptlist[3],divlist[i]);
break;
case #777:
loadScript(scriptlist[4],divlist[i]);
break;
}
}
This worked for me, and may help you.
$.ajax({
async: false,
url: "jui/js/jquery-ui-1.8.20.min.js",
dataType: "script"
});
Basically, I just bypassed the shorthand notation and added in the async: false
As I said, it's relatively easy to chain Ajax calls with promise objects. Now, it don't see why the scripts have to be loaded one after the other, but you will have a reason for it.
First though I would get rid of the switch statement if you are only calling the same function with different arguments. E.g. you can put all the script URLs in a map:
var scripts = {
'#FFF': '...',
'#000': '...'
// etc.
};
You can chain promises by simply returning another promise from a callback passed to .then [docs]. All you need to do is start with a promise or deferred object:
var deferred = new $.Deferred();
var promise = deferred.promise();
for (var i in divlist) {
// we need an immediately invoked function expression to capture
// the current value of the iteration
(function($element) {
// chaining the promises,
// by assigning the new promise to the variable
// and returning a promise from the callback
promise = promise.then(function() {
return loadScript(
scripts[$element.css("background-color")],
$element
);
});
}($('#' + divlist[i])));
}
promise.done(function() {
// optional: Do something after all scripts have been loaded
});
// Resolve the deferred object and trigger the callbacks
deferred.resolve();
In loadScript, you simply return the promise returned from $.getScript or the one returned by .done:
function loadScript(script_url, $element){
// Unrelated stuff here!!!
return $.getScript(script_url).done(function(){
// Unrelated stuff here
// do something with $element after the script loaded.
});
}
The scripts will all be called in the order the are access in the loop. Note that if divlist is an array, you really should use normal for loop instead of a for...in loop.
Do you know that $.getScript accepts a callback function that is called synchronously after the script is loaded?
Example:
$.getScript(url,function(){
//do after loading script
});
I have 2 more solutions: a pure js one and one for multiple js load.
Try this way, create array with deferred objects and used $.when with "apply"
var scripts = [
'src/script1.js',
'src/script2.js'
];
var queue = scripts.map(function(script) {
return $.getScript(script);
});
$.when.apply(null, queue).done(function() {
// Wait until done, then finish function
});
var getScript = function(url) {
var s = document.createElement('script');
s.async = true;
s.src = url;
var to = document.getElementsByTagName('script')[0];
to.parentNode.insertBefore(s, to);
};
#Felix Kling's answer was a great start. However, I discovered that there was a slight issue with the overall attached .done() at the end of the .getScripts() returned result if I wanted to "functionalize" it. You need the last promise from the chained .getScript() iterations from within the loop. Here's the modified version of his solution (thank you, BTW).
Plugin:
(function ($) {
var fetched = new function () {
this.scripts = [];
this.set = [];
this.exists = function (url) {
var exists = false;
$.each(this.set, function (index, value) {
if ((url || '') === value) {
exists = true;
return false;
}
});
return exists;
};
this.buildScriptList = function () {
var that = this;
that.set = [];
$('script').each(function () {
var src = $(this).attr('src') || false;
if (src) {
that.set.push(src);
}
});
$.merge(this.set, this.scripts);
return this;
};
},
getScript = $.getScript;
$.getScript = function () {
var url = arguments[0] || '';
if (fetched.buildScriptList().exists(url)) {
return $.Deferred().resolve();
}
return getScript
.apply($, arguments)
.done(function () {
fetched.scripts.push(url);
});
};
$.extend({
getScripts: function (urls, cache) {
if (typeof urls === 'undefined') {
throw new Error('Invalid URL(s) given.');
}
var deferred = $.Deferred(),
promise = deferred.promise(),
last = $.Deferred().resolve();
if (!$.isArray(urls)) {
urls = [urls];
}
$.each(urls, function (index) {
promise = promise.then(function () {
last = $.getScript(urls[index]);
return last;
});
});
if (Boolean(cache || false) && !Boolean($.ajaxSetup().cache || false)) {
$.ajaxSetup({cache: true});
promise.done(function () {
$.ajaxSetup({cache: false});
});
}
deferred.resolve();
return last;
}
});
})($);
You can ignore the fetched function (I implemented it to reduce potential redundant calls - which is why I hijacked .getScript()) and see where the variable last is set inside the .getScripts() method. It defaults to a resolved deferred object, so that if the urls array is empty, it's passed to the returned result to attach the outer .done() call to. Otherwise, it will inevitably be assigned the last promise object from the chained .getScript() calls and thus will ensure everything will remain synchronous from outside the function.
Returning the initially created deferred object will not work if you resolve it before returning it back to the invoker (which is what you're supposed to do per jQuery's official documentation).
Example:
function loadStuff(data) {
var version = {
'accounting': '1.2.3',
'vue': '1.2.3',
'vueChart': '1.2.3'
};
$.getScripts([
'https://cdnjs.cloudflare.com/ajax/libs/accounting.js/' + version.accounting + '/accounting.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue/' + version.vue + '/vue.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue-chartjs/' + version.vueChart + '/vue-chartjs.min.js'
], true)
.done(function () {
// do stuff
})
.fail(function () {
throw new Error('There was a problem loading dependencies.');
});
}
Just create a script node, set its src property to the JS you want to load then append it to the head:
var myScript = document.createElement('script');
myScript.src = "thesource.js";
document.head.appendChild(myScript);
this is what I do
function loadJsFile(filename) {
$.ajaxSetup({
cache: true
});
var dloadJs = new $.Deferred();
$.when(dloadJs).done(function () {
$.ajaxSetup({
cache: false
});
});
dloadJs.resolve(
$.getScript(filename, function () { })
);
}