This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I'm sure my problem is based on a lack of understanding of asynch programming in node.js but here goes.
For example: I have a list of links I want to crawl. When each asynch request returns I want to know which URL it is for. But, presumably because of race conditions, each request returns with the URL set to the last value in the list.
var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
var url = links[link];
require('request')(url, function() {
console.log(url);
});
}
Expected output:
http://google.com
http://yahoo.com
Actual output:
http://yahoo.com
http://yahoo.com
So my question is either:
How do I pass url (by value) to the call back function? OR
What is the proper way of chaining the HTTP requests so they run sequentially? OR
Something else I'm missing?
PS: For 1. I don't want a solution which examines the callback's parameters but a general way of a callback knowing about variables 'from above'.
Your url variable is not scoped to the for loop as JavaScript only supports global and function scoping. So you need to create a function scope for your request call to capture the url value in each iteration of the loop by using an immediate function:
var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
(function(url) {
require('request')(url, function() {
console.log(url);
});
})(links[link]);
}
BTW, embedding a require in the middle of loop isn't good practice. It should probably be re-written as:
var request = require('request');
var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
(function(url) {
request(url, function() {
console.log(url);
});
})(links[link]);
}
Check this blog out. A variable can be passed by using .bind() method. In your case it would be like this:
var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
var url = links[link];
require('request')(url, function() {
console.log(this.urlAsy);
}.bind({urlAsy:url}));
}
See https://stackoverflow.com/a/11747331/243639 for a general discussion of this issue.
I'd suggest something like
var links = ['http://google.com', 'http://yahoo.com'];
function createCallback(_url) {
return function() {
console.log(_url);
}
};
for (link in links) {
var url = links[link];
require('request')(url, createCallback(url));
}
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I am setting a url to a DOM of a website. Now I could done it by copying the actual url, as below:
document.getElementById('mediaWebUrl').setAttribute('value','https://www.youtube.com/watch?v=hkOTAmmuv_4');
It is not convenient enough. I want to substitute the actual url with a variable. like below:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var activeTab = tabs[0];
var videoUrl = activeTab.url; // or do whatever you need
});
But when I do the job as below:
document.getElementById('mediaWebUrl').setAttribute('value',videoUrl);
it didn't succeed and nothing happened. Why? I confirmed the type of the variable "videoUrl" is a string using alert(typeof videoUrl). As neophyte in Javascript, I really need advice in this simple question. Thank you.
You can write a function and pass the value of the id of the div (where you want to display the video) and the video url, and then call the function to set the attribute.
Sample code:
function setVal(id, val){
var el = document.getElementById(id); //get the element
var attrVal = val; //set the url
el.setAttribute('value', val) //set the value to the url
}
setVal("myVid", "https://www.youtube.com/watch?v=hkOTAmmuv_4"); //call the function
(in this case, myVid is the id)
Your code probably didn't worked because the variable was only locally defined.
You can use your web console to check for errors.
Hope this helps!
I am trying to force the Page Tracking on my site (see: Link). I am using a TMS, but not Google Tag Manager.
What I am hoping to achieve is prepend the market name to the URI which I can pull out of my site's dataLayer (variable = dataLayer.language and thus the new Page Path: dataLayer.language.substring(0,2)+document.location.pathname e.g. /en/pagepath1/pagepath2.
I've created a script below to define this.
There is a small complication here in that some of the pages already have the country/language variation passed in the URL so I have a regular expression to search for the market/language ISO code (lower case) to help perform an if statement that returns document.location.pathname where this is the case.
My script below however, seems to be fine but in testing it is returning "undefined" does anyone why this must be the case?
function PagePath() {
var StandardPagePath = document.location.pathname;
var NewPagePath = dataLayer.language.substring(0, 2) + document.location.pathname;
var LocaleRegExPatt = /^(at|cn|cz|de|dk|en|eu|fr|it|nl|no|pl|pt|pt-br|se)\//
try {
if (StandardPagePath.includes(LocaleRegExPatt)) {
return (StandardPagePath);
} else {
return (NewPagePath)
}
} catch (err) {}
}
My second question is how can I reference this function to pass through to the page tracking i.e. set the page here:
ga('set', 'page', {{PagePath function here}} );
Thanks in advance
My Problem is:
I have an eibport:
www.bab-tec.de/
You can See a live demo in the live section.
Now the problem.
I would like to start or run a javascript on this page with entering the url like:
http:// IP-EIBPORT /#javascript:click(Button)
or in another way.
Can someone helps me?
You may want to setup your script to look for a sepcific hash and run a function based on that.
switch(location.hash){
case "#button":
//click button
break;
case "#other":
//do something else
break;
}
Then use urls like /#button or /#other
The way to do exactly what you want is the following, but it's not recommended!
eval(location.hash.substr(1));
Use a url like /#alert('test'); That will work, but it means anyone can link to your site and execute javascript which is a very unsafe thing to do.
You cannot include JavaScript-invoking code in the URL itself. The best you can do is have a script on the destination page that examines the location, and responds accordingly.
As an example, suppose you wanted to expose sum method via the url. If somebody were to load your page with the following query string: ?method=sum¶ms=5,5, you could have logic in place to look for these parts, and process them.
var methods = {
sum: function () {
var result = 0; i = -1;
while ( ++i < arguments.length )
result += parseInt( arguments[i], 10 );
return result;
}
};
var method = location.search.match(/method=(.*)&/)[1],
params = location.search.match(/params=(.*)$/)[1].split(",");
var result = methods[ method ].apply( this, params );
alert( result );
See it in action: http://jsfiddle.net/jonathansampson/TF5ta/show/?method=sum¶ms=5,5
With complete control over both the client and server side code, I'd like to accomplish the following:
Initiate a server request in a javascript function
Be able to abandon the request (from the user experience perspective) after a specified time
Access information about the response (e.g. either a redirect URL or part of the response body) before exiting the original function (this part is non-negotiable; setting a window interval, for example, will not cut it)
This sounds a lot like multithreading to me, which of course javascript doesn't do. Perhaps there's no solution, but I'm exhausting my options before admitting to that. In the non-working example below, function foo() sets an iframe's src to the url of a page -- redirect.aspx here -- which after a short delay redirects to another page with some UUID in the query string. (Note: it could just as well return the UUID in a hidden field in the response body, or via some other strategy; I have control over this).
Regardless how the server page returns the result, my goal is to access the UUID from the server before foo() exits.
Update: Suggested Unit Test
Though this question appears to be about scope -- and therefore solvable via closures (test pending) -- it's actually about continuity of execution. A successful test would consist of:
Create the foo() function
Assign something.onClick = foo()
foo() somehow initiates a server call and retrieves a URL from the response
foo() then calls window.open(url); using that URL
A window opens in all major browsers (critical case: IE 7, 8 & 9)
I do not currently know of a strategy that can pass this test.
Non-working sample:
<iframe id="aFrame" src="" height="0" width="0"></iframe>
<script type="text/javascript" language="javascript">
function foo() {
var f = document.getElementById("aFrame");
var loc = "http://localhost:8080/redirect.aspx?after=1000";
f.src = loc;
var start = new Date().getTime();
while (elapsedSince(start < 5000)) { // allow for server response
// FAIL: this is never true until after foo() exits:
if (f.src != loc) {
alert(encodeURIComponent(f.src));
return true;
}
}
alert("Timeout");
return false;
}
function elapsedSince(startTime) { // omitted safety checks for brevity:
return new Date().getTime() - startTime;
}
</script>
I'm not an ace at Ajax functions, but according to my understanding they require a callback, which means any return information arrives outside of the initiating function. Fail.
The above strategy doesn't work, per comments in the js code.
Other strategies might include something like using document.createElement() to create the iframe and .insertBefore() to add it to the DOM, but I'm still not certain I would be able both to initiate that and access any response details from within the same iteration of foo().
Does anyone know of any strategy that meets the above criteria?
Here is some code that would pass your "test". It gets a URL from an external page and opens a new window with that URL.
It is non-blocking as you set up a listener for readystatechange events (callback).
function foo() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/pathname', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
window.open(xhr.responseText);
}
};
};
button.onclick = foo;
I have written a PHP script that checks whether a domain is available for registration or not.
To automate the process, I have also written a js script that automatically sends AJAX calls to the PHP script and tells the user whether the domain is available or not, without submitting the form:
$(document).ready(function() {
function Domain() {
this.name = '';
this.dotComRegistered = 1;
this.dotNetRegistered = 1;
this.dotOrgRegistered = 1;
}
Domain.prototype.check = function(input) {
this.name = input;
if (this.name.length >= 3 && this.name.length <= 63) {
$.get('check.php', { d : this.name }, function(json) {
alert(json);
this.dotComRegistered = $.evalJSON(json).com;
this.dotNetRegistered = $.evalJSON(json).net;
this.dotOrgRegistered = $.evalJSON(json).org;
});
}
}
var domain = new Domain();
var input = ''
$('#domain').keyup(function() {
input = $('#domain').val();
domain.check(input);
});
$('form').submit(function() {
input = $('#domain').val();
domain.check(input);
return false;
});
});
As you can see I created an object called Domain which represents a domain name. The object has just one method (besides the constructor) that sends AJAX request to the PHP script (which returns json).
The problem is that the Domain.prototype.check() method doesn't work (I don't get an alert window) and I don't know where's the problem. When I place the AJAX call outside of the method it works, so that isn't the problem.
I'm an OOP beginner so maybe I used some wrong syntax to write the Domain object (I'm reading a book from John Resig about OOP in JavaScript right now).
The #domain is the input field for domain names.
have you stepped through with Firebug? Set a breakpoint at the point the request is made and step through from that point. Where does the dn come from in your code?
Also, is there a specific reason that your Domain function is in $(document).ready()? It doesn't really need to be there (you might consider namespacing your classes too).