I am new to jsonp and I understand that JSONP is a technique which creates a dynamic
<script src="..."> tag, that wraps the returned JavaScript (or JSON object) with a callback function.
But if I am not mistaken, src attribute in a script tag will hold back all further executions until the script loads, so how can it be asynchronous call?
Actually, as you can read up on here and here dynamically created <script src=".."> elements after the DOM has finished loaded will NOT be blocking and by that they will be asynchrounus.. at least in the order they are created.
qutoted from http://calendar.perfplanet.com/2010/the-truth-about-non-blocking-javascript/
When inserting a script dynamically, the non-blocking download begins immediately. The script executes as soon as it is downloaded completely. In most browsers, the order of execution is not guaranteed, though Firefox < 4 and Opera will execute the scripts in the order in which they were inserted. This general approach is supported in all major browsers.
I think your question has two parts.
First, JSONP is essentially not about dynamic script tags, rather dynamic script tags are a technique used hand in hand with JSONP.
JSONP is a method which allows site to load content from different domains than the ORIGIN, exploiting the browser's tolerance towards SCRIPT tags with src pointing to external domains. (You should know this by going through the links given in other answers).
Dynamic SCRIPT tags on the other hand provides an Asynchronous nature to any script be it JSONP or otherwise.
Point is, whenever a browser hits a SCRIPT tag on a document, it stops most other activities (rendering DOM specially) until that script is download. This affects users experience on how responsive the site is. Effect of this is even worse if the script is not directly contributing to the primary content of the site (such as Google Ads, Tweets, or Facebook Timeline (Asuming you are not Mark Z. :P), etc)
To avoid this problem, you can inject dynamic SCRIPT tags to the page once it has fully loaded on the browser (i.e ready/loaded event). Then the browser will silently load the new script, but user has the full page (almost) rendered for him giving impression of quick loading. In that sense dynamic scripts can be asynchronous to page loading.
However, in practice most of scripts used in this way are JSONP scripts residing on different domains, although it's not a requirement.
Hope this makes sense.
For TRUE async script loading you should look into HTML5 sync attribute:
The call is asynchronous, yes. Maybe you are confusing the behaviour of a script tag when the page is loading and when the page is already loaded.
As the page is being loaded by the browser, all HTML tags with resources (image tags, link tags, etc...) have their resources downloaded assynchronously and don't interrupt the browser rendering task. This has the improvement of optimizing the performance of the page rendering.
The only tag which doesn't follow this rule is the script tag. Because the browser has to ensure the order of the scripts, it will not load them in parallel. Additionally, the browser has to count with dynamic alterations on the HTML document made from the script, using document.write, and for this reason it will evaluate the script as soon as it is downloaded. So, this is the default behaviour from the browsers concerning script tags with src file: they will block the rendering of the page, be downloaded in sequence and will be evaluated as soon as they have been loaded. There are techniques to avoid this, like placing the scripts at the bottom of your document (scripts will only be downloaded and evaluated after the document has been rendered) or using the new HTML5 script tag attributes "async" and "defer": http://blogs.microsoft.co.il/blogs/gilf/archive/2011/12/29/the-async-and-defer-script-attributes-in-html5.aspx .
Going back to JSONP: yes, it is asynchronous in the way that it is not blocking any further browser behaviour (page is already rendered). This is the asynchronicity regular AJAX calls provide.
JSONp includes don't really work like AJAX calls since they're a bit of a hack. If I were pressed to put them in either box I'd go with "asynchronous".
I guess the most important trait is that the 'return value' will show up in another event.
You can't write:
var return_value = do_jsonp("my function");
You have to write this instead: (or use some sort of promise library)
do_jsonp("my function", function (return_value) {});
So, when the script in the JSONp resource actually gets executed is not relevant. All that is relevant is that it happens in a different event.
This related question should shed some light on your question.
Script nodes added dynamically using javascript are executed asynchronously and they won't block execution of other scripts.
you are kind of misinterpreting the word "asynchronous". the javascript excecution is asynchronous, which means the every line of javascript-code which comes after you inject your script-tag is executed.
example:
var yourCB = function(result) { ... }
// i use the script injection from neiker here (without the async = true, to illustrate that this has nothing to do with the asynchronous nature JSONP
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.src = 'http://domain/api/...?callback=yourCB';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
// everything after the script insertion is excecuted, even if your jsonp-callback hasnt been called. as soon as your script-request has finished yourCB is called.
alert('THIS IS ALERTED EVEN IF yourCB HASNT BEEN CALLED');
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = 'http://domain/api/...?callback=something';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
Anyway, can't you use jQuery? If not, try this: https://github.com/ded/reqwest
Related
I'm learning javaScript and just came on the topic of promises , async await , fetch etc.
While reading a tutorial online , It described the following code as asynchronous
function loadScript(src) {
let script = document.createElement('script');
script.src = src;
document.head.append(script);
}
I have learned that javascript hands over all the asynchronous work to the browser , like http request as they take time and might block the flow in javascript.
Please point out to me how the above code is asynchronous?
Which part of it is asynchronous? Is it because we are using document object?
are all methods in document object asynchronous and handled by the browser?
or is it something else?
here is the link to the tutorial, sorry if I misunderstood something.https://javascript.info/callbacks
It's asynchronous because you're fetching the script code from the server, using the src URL. That has to send a network request, and the code won't be inserted into the DOM and executed until the server responds.
Modifying the DOM itself is synchronous, but a number of elements can cause network requests to be sent and wait. Examples are <script>, <img>, and <iframe>.
The script tag itself needs to load the url so this is the part that is asynchronous. Scriptloads are handled by the Browser.
If you're referring to the fact that dynamically created script elements behave as though they have their async attribute set, that's outlined in the spec:
A script element has a flag indicating whether the element will be "non-blocking". Initially, script elements must have this flag set. It is unset by the HTML parser and the XML parser on script elements they insert. In addition, whenever a script element whose "non-blocking" flag is set has an async content attribute added, the element's "non-blocking" flag must be unset.
...but even if the script element doesn't have async set, the download of the source code is still going to happen asynchronously; async simply says "don't block further HTML parsing or JavaScript execution while waiting for this to download." The code that injects the script will still finish running, but any subsequent script elements on the page will wait for blocking scripts to download.
I created a vanillaJS script that is used by several other websites. The script-server went down for a couple of minutes last week and the result was that several of the sites using the script were not loading correctly (slow, pages not loading at all, errors, etc.) because the sites kept on "waiting" for the external script to load.
I noticed ALL these sites had the script in de head. I suggested moving the script-tag to the footer plus adding the 'async' attribute. But is this the best solution?
Sidenotes:
Can't use jQuery
Can't use a framework like Angular or React
Preferably don't use additional JS on the site itself
The script created it's own content and does not rely on anything on
the page it's served on. It simply created a div with content from a
datababase staticly served in very basic JS to avoid cross-site
errors.
Thanks in advance.
async will make the script to load asynchronously and will be be executed while the page is reading.
defer will make the script to execute once the page is loaded although it's highly depends on the browser at to my tests IE9, IE8 supports this. You can make a quick check with this defer in fiddler
But there is one more alternative to have the script tag at the bottom as
<script>
window.onload = function() {
var element = document.createElement("script");
element.src = "vanillaJS .js";
document.body.appendChild(element);
};
</script>
But i guess these will not solve your problem where the script is not accessible cause of server fault. I suggest to have the local copy of the script in the website folder and reference it.
I am trying to analyze some JavaScript code for which I make use of function rewriting so that calls to a JavaScript library go through my JavaScript code. My JavaScript code is part of a Chrome Extension. From a Chrome extension content script, I install/inject the code into the target page's DOM.
This works fine for functions that are induced after the load of page. The library calls go through my function. But, there's JavaScript code that runs while the page is actually loading (probably while the DOM is being rendered). This happens before my custom script is injected. This way, the function calls before the custom script is injected are lost to me, or those JavaScript calls do not go through my function.
I make use of Content Script to actually inject other JavaScript by appending to the DOM as mentioned in the following Stack Exchange question:
Insert code into the page context using a content script
I know I can cause the loading time of Content Script to be at the start/end of the DOM but this is another script file that I append to the DOM of the target page. I do not seem to understand how to control it.
The problem explained in Is it possible to run a script in context of a webpage, before any of the webpage's scripts run, using a chrome extension?
is exactly the same, but the solution does not seem to work. My intention is to make the injected script execute before any JavaScript code executes from the webpage. By specifying document_start in manifest.json, content script execution can be made to run before the webpage, but not the script that I inject through the content script (injecting script as explained in first link). This injected script is not running in any specific manner with respect to the webpage
Manifest.json:
Manifest file has the content script content.js added at document_start, so content.js is run before the target webpage (underlying page) runs.
"content_scripts":[
{
"matches":["<all_urls>"],
"js":["content.js"],
"run_at":"document_start",
"all_frames":false
}
],
content.js:
content.js has the below code with which I add the main.js to the DOM, so that I am actually able to interact with the JavaScript that is in the target page's environment. I do this from a different file and attach it to the DOM because I cannot interact with the target page's JavaScript through the Content Scripts, since they both do not interfere with each other.
To explain further, main.js has some JavaScript that intercepts JavaScript calls during the execution of JavaScript in target page. JavaScript in target page makes calls to a library and I intend just to write a wrapper on those library functions.
var u = document.createElement('script');
u.src = chrome.extension.getURL('main.js');
(document.head||document.documentElement).appendChild(u);
u.onload = function() {
u.parentNode.removeChild(u);
};
I expect that main.js is available in the target page's domain and any of the scripts in the target page, since I inject it through the content script that is run at document_start.
Assume I have a call to some JavaScript function like this in my target page HTML, someJSCall() is defined by the target page's domain.
<html onLoad="someJSCall( )">
In this scenario, main.js (code injected through my Chrome extension) is already available. So calls to the JavaScript library from someJSCall() function go through main.js wrapper functions.
This works fine.
The problem is when there are IIFE (immediately invoked function expressions) defined in the target page's JavaScript. If these IIFE calls make library calls, this does not go through my main.js interceptions. If I look at the files loaded in the browser through Chrome Dev Tools, I see that main.js is still not loaded while IIFE calls are executing.
I hope I have explained the problem in detail.
Based on the additional information you added to the question about 2.5 weeks after I answered, you are adding code to the page context by including a "main.js", which is a separate file in your extension, using a <script> that looks something like:
<script src="URL_to_file_in_extension/main.js"/>
However, when you do that you introduce an asynchronous delay between when the <script> is inserted into the page and when the "main.js" is fetched from the extension and executed in the page context. You will not be able to control how long this delay is and it may, or may not, result in your code running prior to any particular code in the page. It will probably run prior to code that has to be fetched from external URLs, but may not.
In order to guarantee that your code runs synchronously, you must insert it in a <script> tag as actual code, not using the src attribute to pull in another file. That means the code which you want to execute in the page must exist within the content script file you are loading into the page.
Needing to execute code in the page context is a fairly common requirement. I've needed to do so in browser extensions (e.g. Chrome, Firefox, Edge, etc.) and in userscripts. I've also wanted to be able to pass data to such code, so I wrote a function called executeInPage(), which will take a function defined in the current context, convert it to text, insert it into the page context and execute it while passing any arguments you have for it (of most types). If interested, you can find executeInPage() in my answer to Calling webpage JavaScript methods from browser extension and my answer to How to use cloneInto in a Firefox web extension?
The following is my original answer based on the original version of the question, which did not show when the content script was being executed, or explain that the code being added to the page was in a separate file, not in the actual content script.
You state in your question that you "can handle the loading time of Content Script to be at the start/end of the DOM", but you don't make clear why you are unable to resolve your issue by executing your content script at document_start.
You can have your script injected prior to the page you are injecting into being built by specifying document_start for the run_at property in your manifest.json content_scripts entry, or for the runAt option passed to chrome.tabs.executeScript(). If you do this, then your script will start running when document.head and document.body are both null. You can then control what gets added to the page.
For chrome.tabs.executeScript() exactly when your script runs depends on when you execute chrome.tabs.executeScript() in relation to the process of loading the page. Due to the asynchronous nature of the processing (your background script is usually running in a different process), it is difficult to get your script consistently injected when document.head and document.body are both null. The best I've accomplished is to have the script injected sometimes when that is the case, and sometimes after the page is populated, but prior to any other resources being fetched. This timing will work for most things, but if you really need to have your script run prior to the page existing, then you should use a manifest.json content_scripts entry.
With your content script running prior to the existence of the head and body, you can control what gets inserted first. Thus, you can insert your <script> prior to anything else on the page. This should make your script execute prior to any other script in the page context.
Here's the scenario, not sure what I'm missing.
Page A.htm makes an ajax request for page B.htm, and inserts the response into the page.
Page B.htm contains links to several other JS files, many of which contain a document.ready() function to initialize them.
This works fine when A.htm and B.htm are on the same server but not when they are on different servers.
What I think I'm seeing here, is that when page A and B are on different servers (cross domain ajax), the external resources are being returned asynchronously, or at least out of order, so scripts are executing expecting JQuery.UI to be loaded already, when it is not.
Appreciate any pointers or advice. Apologies for the poor explanation.
You are injecting HTML + script tags via jQuery. In this case *:
HTML content except scripts are injected in the document
Then all scripts are executed one by one
If a script is external then it is downloaded and executed asynchronously
Therefore an external or inline script that depends on jQuery UI might execute before jQuery UI.
One possible solution is to change the way your pages work:
Get rid of external scripts in pageb.html but keep inline scripts
Load the required scripts in pagea.html
Load pageb.html
Another solution is to roll your own jQuery function that will:
Strip all <script src> elements from HTML
Download and execute those scripts in order
Inject the remaining HTML
* The exact behavior is not documented. I had to look into the source code to infer the details.
you are correct in your impression that the issue is a difference in how the requests are handled cross-domain.
Here is a link to get you on the right track : How to make synchronous JSONP crossdomain call
However, you will have to actually re-achitect your solution somewhat to check if the resource has been loaded before moving on. There are many solutions (see the link)
You can set a timer interval and check for something in the dom, or another reasonable solution (despite it's lack of efficiency) is to create a "proxy" serverside (eg php) file on your server and have that file do the cross-domain request, then spit out the result.
Note that since jquery UI is a rather large file, it's conceivable that the cross-domain request finishes first, and executes immediately, even though jqueryUI is not loaded yet. In any case, you're going to have to start thinking about having your app react rather than follow a sequence.
Say I get a response to a request for an AJAX load of data with a mix of JavaScript and HTML, e.g.:
<script>window.alert('Hello World!');</script>
<p>This is a paragraph. Lorem ipsum dolor sit amet...</p>
If I just place that response into a div or other container, the script doesn't get executed automatically. I know this can be done via the eval() function (as noted in the example below), but eval is evil, so how can I do this properly? Note: I am not using jQuery.
The following is an example of the AJAX loader:
function Load(id,url){
var ajax=new XMLHttpRequest();
ajax.onreadystatechange=function(){
if(ajax.readyState!=4)return;
var obj=document.getElementById(id);
if(!obj)return;
obj.innerHTML=ajax.responseText;
// load any scripts
var s=obj.getElementsByTagName('script');
for(var i=0;i<s.length;++i)window.eval(s[i].innerHTML); // <-- bad
}
ajax.open("GET",url,true);
ajax.send(null);
}
Please note that you're taking input from the user and running it in the context of a script on your site. So the script can do anything that JavaScript running on your browser/domain would have the ability to do (including cookie stealing, XSS, drive-by malware, etc.).
The only thing you can realistically do to mitigate the risks is to not eval() user-provided content. I'd suggest to consider the following alternatives:
Use iframe as an environment to run user's script:
http://dean.edwards.name/weblog/2006/11/sandbox/
Use Caja. It allows websites to safely embed DHTML web applications from third parties, and enables rich interaction between the embedding page and the embedded applications. It uses an object-capability security model to allow for a wide range of flexible security policies.
http://code.google.com/p/google-caja/
eval isn't particularly evil in this scenario, it's not a lot different than, say dynamically adding a script tag which pulls down a .js file and runs it. That said, there are other options, for instance you can dynamically create a script tag, create a text node with the contents of the script tag you pulled down, and add that to the document. Unlike the innerHTML technique, that will actually run the contents. The only advantage over eval, really, is that you might get a more meaningful stack trace etc if it crashes or has a syntax error.
var newScriptTag = document.createElement('script');
newScriptTag.appendChild(document.createTextNode(
origScriptTag.innerHTML)
document.body.appendChild(newScriptTag);
I solved this today by putting my JavaScript at the bottom of the response HTML.
I had an AJAX request that returned a bunch of HTML that was displayed in an overlay. I needed to attach a click event to a button in the returned response HTML/overlay. On a normal page, I would wrap my JavaScript in a "window.onload" or "$(document).ready" so that it would attach the event handler to the DOM object after the DOM for the new overlay had been rendered, but because this was an AJAX response and not a new page load, that event never happened, the browser never executed my JavaScript, my event handler never got attached to the DOM element, and my new piece of functionality didn't work. Again, I solved my "executing JavaScript in an AJAX response problem" by not using "$(document).ready" in the head of the document, but by placing my JavaScript at the end of the document and having it run after the HTML/DOM had been rendered.