JavaScript image onload: inconsistent access to resolve values from Promise [duplicate] - javascript

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I wrote this simple function to check if certain images in an array are loadable, and placed the function close to the end of the JS file:
function checkImage(img) {
console.log(img);
return new Promise(resolve => {
var image = new Image();
var imgstatus = false;
image.onload = function() {
console.log(image);
console.log("img loaded");
imgstatus = true;
resolve(imgstatus);
};
image.onerror = function() {
console.log(image);
console.log("img error");
imgstatus = false;
resolve(imgstatus);
};
image.src = img;
});
}
It might be an overkill because I want to do was to check if each image file defined in a user-defined ('user' as in who can also edit the source-code) image array is loadable.
FYI, the array looks like this globally defined at the top of the JS file:
var userarray=["pics/blank.png", "pics/ape.png", "pics/orange.png", "pics/pear.png", "pics/blueberries.png", "pics/peach.png", "pics/grapes.png"];
So at the beginning of the same JS script I have this implemented within an initializing function (the first function in the JS file):
if (userarray != undefined && userarray.length == (mylength + 1))
{
var allfilespassed = 0;
for (var i=0; i < userarray.length; i++)
{
var tmp;
checkImage(userarray[i]).then(function(imgstatus){tmp=imgstatus; console.log(imgstatus);});
console.log(tmp);
if(!tmp)
allfilespassed = -1;
console.log(allfilespassed);
}
if(allfilespassed == 0) // all images passed
imagearray = userarray;
}
}
What is intriguing and frustrating to me is that in the console.logs:
pics/blank.png //perhaps from top level console.log(img); from the checkImage(img) function.
undefined //from console.log(tmp);
-1 //from console.log(allfilespassed);
pics/ape.png
undefined
-1
pics/orange.png
undefined
-1
pics/pear.png
undefined
-1
pics/blueberries.png
undefined
-1
pics/peach.png
undefined
-1
pics/grapes.png
undefined
-1
pics/ape.png //from console.log(image); inside image.onerror of the checkImage(img) function.
img error
false //from console.log(imgstatus); inside the .then() call, back up in the initialize function.
pics/blank.png
img loaded
true
pics/orange.png
img loaded
true
pics/pear.png
img loaded
true
pics/blueberries.png
img loaded
true
pics/peach.png
img loaded
true
pics/grapes.png
img loaded
true
Obviously, the values of variable imgstatus were undefined when I run the initialize function but the inner values of function checkImage came up later to show the results are what I expected (the "false" and "true"s came up later), i.e. "ape.png" is the only misnamed file in the array. It has to do with the order and priority of functions and objects are processed in the DOM - which I may also need some pointer, in case I have been doing other things correctly.
I also tried creating something using async and await, but there was not much help: (i.e. with same output from console.log)
async function waitImage(i) {
return await checkImage(i).then(function(imgstatus){return imgstatus;});
};
tmp = waitImage(userarray[i]);
What I got at the top of console.log instead of those undefined entries where a bunch of Promise { "pending" } objects.
So inside the same .then() of a promise, values are accessed at different time, leading to resolve value first being undefined, and then later on, in the same .then() the values are shown correctly.
No previous posts I have browsed through have explained clearly about the timeline in terms of DOM events when Promise and its resolution happens.
Please let me know your suggestions, especially on how to make it work.

Related

Access variable outside of an eventListener in Javascript [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I am building an app with Javascript and OpenLayers and while it's going fairly well, I am stuck at what I feel could be an easy problem for someone experienced.
Here is part the code where the problem lives :
const baseLayerElementsRight = document.querySelectorAll(
'[name="baseLayerRadioButtonRight"]'
);
let layerRender = {};
for (let baseLayerElementRight of baseLayerElementsRight) {
baseLayerElementRight.addEventListener(
'change',
function() {
let baseLayerElementValueRight = this.value;
layerGroupRight.getLayers().forEach(function(element) {
let baseLayerNameRight = element.get('title');
element.setVisible(
baseLayerNameRight === baseLayerElementValueRight
);
let elementVisibility = element.get('visible');
if (elementVisibility) {
layerRender = element;
console.log(layerRender);
}
});
},
false
);
}console.log(layerRender)
So Basically, I need to apply a method on the "layerRender" object variable outside of the event callback and I am quite new to programming and I struggle to find a solution to access the variable value
The console.log in the callback's event output the Object I want everytime I click on a given radio type input, but the console.log outisde it outputs an empty Object and of course don't update everytime I the event is happening.
How could I do to access the layerRender value outside of the callback?
Thanks a lot
your last console.log only happens once as the code runs through... you first declare the variable. Then you add an eventListener in a for loop then finally do console.log code executes. and it executes NOT when you're clicking
const baseLayerElementsRight = document.querySelectorAll(
'[name="baseLayerRadioButtonRight"]'
);
let layerRender;
for (let baseLayerElementRight of baseLayerElementsRight) {
baseLayerElementRight.addEventListener(
'change',
function() {
console.log('Click happened');
layerGroupRight.getLayers().forEach(function(element) {
let baseLayerNameRight = element.get('title');
element.setVisible(
baseLayerNameRight === this.value
);
let elementVisibility = element.get('visible');
if (elementVisibility) {
layerRender = element; // assign element to layerRender
// console.log if element is visible only
console.log(layerRender);
}
});
},
false
);
}
console.log(layerRender); // Happens when function initialise NOT when click happens

Arraylength 0 but 13 objects in array [duplicate]

This question already has answers here:
Is Chrome’s JavaScript console lazy about evaluating objects?
(7 answers)
Closed 5 years ago.
Javascript: I have a dynamically filled array named resultsAuthor, when I log this object, I get this. (logged it because I need the length to loop through the object and this wasn't working) 1
But when I try to log resultsAuthor.length, I get 0.
Anyone know what I'm doing wrong?
This is in my utils.js
From there I call it in my main.js like this:
var searchCriteria = document.querySelector('[title="tbxZoeken"]').value;
var searchAuthor = new Utils.GetBookinfoByAuthor(searchCriteria);
var resultsAuthor = searchAuthor.loadData();
console.log(resultsAuthor); // returns the object
console.log(resultsAuthor.length); // returns 0
Thanks!
Your ajax call has probably not finished loading when you print resultsAuthor.length, so it return 0.
If you print resultAuthor this is still a reference to the original variable, so it gets printed correctly.
To fix this you can use a callback function or a promise. With a callback you could do something like this:
In your utils.js
GetBookinfoByAuthor: function (auteur) {
//...
this.loadData = function(callback) {
// Rest of your code...
// Request returned sucessfully
if (xhr.status === 200) {
//Rest of your code....
for (var i = 0; i < data.length; i++) {
// add all books to array
}
// All books a loaded
callback(books)
}
}
}
In your main.js
var searchCriteria = document.querySelector('[title="tbxZoeken"]').value;
var searchAuthor = new Utils.GetBookinfoByAuthor(searchCriteria);
searchAuthor.loadData(function() {
console.log(resultsAuthor);
console.log(resultsAuthor.length);
});

Function should return object, but it's returned as undefined [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 5 years ago.
i try to get image width and height returned from a function, but every time it get's returned as undefined, if i console.log values in the function it shows up, but it returns undefined every time.
The main problem i suspect is with scope, because it is a nested function, but i am not really sure, if thats the case.
JS code
//get image width and height
$(document).ready(function(){
//object method
function dimensions($width, $height){
this.getWidth = $width;
this.getHeight = $height;
}
//gets image data
function readURL(input) {
var reader = new FileReader();
var image = new Image();
reader.onload = function (e) {
image.src = e.target.result;
}
reader.readAsDataURL(input.files[0]);
return image;
}
//gets image dimensions (width height)
function getImageDimensions($img){
var $dimensions;
$img.onload = function(){
$dimensions = new dimensions($img.width, $img.height);
}
return $dimensions;
}
$("#imgInp").change(function(){
alert(getImageDimensions(readURL(this)));
});
});
Html code
<form id="form1" runat="server">
<input type='file' id="imgInp" accept="image/*"/>
</form>
Can anyone explain to me why it doesn't return a object, but returns undefined?
that's because your image doesn't have width or height until it is rendered in the DOM.
So you are right to put the instanciation in a onload handler.
The thing is that handler is triggered only when your images load, but your execute the return line before.
In other words you execute this line
return $dimensions;
before this function
$img.onload = function(){
$dimensions = new dimensions($img.width, $img.height);
}
put the return in $img.onload like this:
$img.onload = function(){
return new dimensions($img.width, $img.height);
}

Difficulty in understanding javascript coding

i'm facing difficulty in understanding what this following code does. could anyone here please help me out in understanding this piece of code?
var PnPResponsiveApp = PnPResponsiveApp || {};
PnPResponsiveApp.responsivizeSettings = function () {
// return if no longer on Settings page
if (window.location.href.indexOf('/settings.aspx') < 0) return;
// find the Settings root element, or wait if not available yet
var settingsRoot = $(".ms-siteSettings-root");
if (!settingsRoot.length) {
setTimeout(PnPResponsiveApp.responsivizeSettings, 100);
return;
}
}
var PnPResponsiveApp = PnPResponsiveApp || {};
The above line ensures that the PnPResponsiveApp variable gets its old value if it already exists, otherwise it's set to a new object.
PnPResponsiveApp.responsivizeSettings = function () {
Here, a new function is created.
// return if no longer on Settings page
if (window.location.href.indexOf('/settings.aspx') < 0) return;
If the URL of the current page isn't the settings page, then the function exits immediately.
// find the Settings root element, or wait if not available yet
var settingsRoot = $(".ms-siteSettings-root");
This gets all elements with a class of .ms-siteSettings-root.
if (!settingsRoot.length) {
setTimeout(PnPResponsiveApp.responsivizeSettings, 100);
return;
}
If any elements were found (if the length of the node list is not zero), then call the PnPResponsiveApp.responsivizeSettings function in 100 milliseconds.
Very easy code basically, I'll explain what's going on:
var PnPResponsiveApp = PnPResponsiveApp || {};
This is very common way to see if the variable is already defined and if not, avoid throwing error and equal it to an empty object, It's used in many frameworks and library, very safe way to check if the var is there already... look at here for more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators
PnPResponsiveApp.responsivizeSettings = function () {};
This is basically a simple function but attached to the object PnPResponsiveApp - if just responsivizeSettings = function () {}; it's attached to window Object
if (window.location.href.indexOf('/settings.aspx') < 0) return;
this is Checking if the Link in the linkbar has settings.aspx - indexOf return -1 if it doesn't contain the string, so if it's not settings.aspx it returns -1 that's smaller than 0 and then the whole function return ... the second return basically return undefined
var settingsRoot = $(".ms-siteSettings-root");
This is basically look for all element with class of ms-siteSettings-root and equal them to variable settingsRoot, it could be a single DOM or multiple...
if (!settingsRoot.length) {
and this basically check if any DOM element has ms-siteSettings-root class, length return a Number, so if it's not there, it returns 0, if there is return 1,2,3 etc... and 0 is equal to False in JavaScript and bigger than 0 is equal to True, so this way we can check if it's there...
setTimeout(PnPResponsiveApp.responsivizeSettings, 100);
so if the settingsRoot is there, we execute this function block and with setTimeout we wait 100ms... setTimeout always works in this manner, setTimeout(function(), time); and the same return happens at the end...
Hope it's informative enough...

why does setting `this.property` in a function in a class make a new property but getting `this.property` uses the class property? [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 7 years ago.
I have a class in javascript with a property (is_initialized) and a function (isInitialized).
Class definition :
function Test()
{
this.is_initialized = { obj: { isInitialized: 'notyet' } };
this.isInitialized = function( ref )
{
if ( !ref.obj )
{
console.log( 'now: ' + JSON.stringify( this.is_initialized ) );
/*line 9*/ this.is_initialized.obj.isInitialized = ref.isInitialized.toString();
console.log( ref );
}
else {
bluetoothle.isInitialized( this.isInitialized );
/*line 14*/ ref.obj = this.is_initialized.obj;
}
};
}
bluetoothle.isInitialized is a function in a cordova plugin (no knowledge of cordova is required for answering this question), it returns an object { isInitialized : true/false } and will pass the first if-statement whilst my call to isInitialized() will execute the else.
Question :
Why does this.is_initialized on line 9 create a new property inside the function isInitialized while this.is_initialized on line 14 uses the property is_initialized in Test()?
Shouldn't it both use the property inside Test() or use a (new) variable inside isInitialized()?
And if it 'just' behaves like this, what can i do to deal with it?
Code i ran :
var t = new Test();
var r = {obj:{isInitialized:'nope'}};
t.isInitialized(r);
// console.log( 'now: ' + JSON.stringify( this.is_initialized ) ); on line 8:
// now: {"obj":{"isInitialized":"false"}}
// console.log( ref ); on line 10:
// Object {isInitialized: false}
console.log(JSON.stringify(r));
// {"obj":{"isInitialized":"notyet"}}
console.log(JSON.stringify(t));
// {"is_initialized":{"obj":{"isInitialized":"notyet"}}}
What just happened is this:
i made a new instance of Test() and named it t.
i made an object with matching structure of is_initialized in Test() but with a different value.
i called the function with r as parameter.
code in the else executes.
asynchronous function with isInitialized as callback is called.
the function created a reference between the existing is_initialized and r.
the async function calls isInitialized and executes the code in the if.
it logs the current value of this.is_initialized on line 8, somehow it gets this.is_initialized after line 9 is executed.
line 9 executes, creating a new variable named is_initialized inside isInitialized() while i want it to set is_initialized in Test() and not create a new variable that dies when the function is done executing.
it logs the object that was put into this.is_initialized.obj.isInitialized.
i log r and see that it contains the initial value of Test.is_initialized.
i log t and see that is_initialized's value is still initial.
Info :
If you want to test it yourself to answer my question of the why? and the how do i deal with it? but need some code for bluetoothle.isInitialized just use this:
var bluetoothle = {isInitialized:function(){setTimeout(function(){func({isInitialized:false});},20);}};
// to execute:
bluetoothle.isInitialized(/*callbackfunction*/);
I would like to thank you for reading this long question.
You're in a function. Inside a function (unless it's declared as Test.prototype.isInitialized), the scoping rules for 'this' are different. This is one of the gotchas that ES6 aims to eliminate. (If you added "use strict"; at the top of the function most browsers would tell you this.)
Declare var self = this in Test and use self inside your interior function and you should get the result you want. #squint has pretty much already said this.

Categories

Resources