Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery cross-domain ajax: callback when executed

Background

I'm loading and executing a script from another server (cross domain) via jQuery's .ajax(...) call.

There is a bit of code that needs to be executed after the code from the other server has been executed, because otherwise some objects are 'undefined', yet.

Maybe important: The remote code does contain another getScript(...) call. And I have to wait for this code to be executed as well. I cannot simply load this second script from my code, because its source is dynamic (i.e. depends on some results of the remote script).

Did not work: success callback

Apparently, a success callback is called after the remote code is laoded, but before the remote code is executed.

# coffee script

executeLater = ->
  # this bit of code needs to run after the remote code has been executed.
  console.log("yehaa!")

$.getScript("http://example.com/script-from-other-server.js")
.success(executeLater)  # this gets executed when the remote script is loaded,
                        # but before the remote script is executed.

Did not work: async: false

Apparently, the async attribute is ignored for cross-domain requests, as stated here in the jQuery documentation: http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings

Furthermore, I would like to avoid the async: false setting, because it is said to block the browser.

# coffee script

executeLater = ->
  # this bit of code needs to run after the remote code has been executed.
  console.log("yehaa!")

$.ajax(
  dataType: 'script',
  url: 'http://example.com/script-from-other-server.js',
  async: false   # does not work because the request is cross domain
)
.success(executeLater)

Did not work: $.when(...).then(...)

Using jQuery's when-then mechanism, apparently, the then code is executed before the when block is executed.

# coffee script

executeLater = ->
  # this bit of code needs to run after the remote code has been executed.
  console.log("yehaa!")

$.when( $.ajax(
  dataType: 'script',
  url: 'http://example.com/script-from-other-server.js',
) ).then(executeLater)

EDIT: Did work, but unusable: ajax both scripts

I can't do this in production, as I said above in the 'background' section, but if I reduce all cases to one and load the second script, which is usually executed by the remote script, in my own script, all works fine.

# coffee script

executeLater = ->
  # this bit of code needs to run after the remote code has been executed.
  console.log("yehaa!")

$.getScript("http://example.com/script-from-other-server.js")
.success( ->
  $.ajax(
    dataType: 'script',
    cache: true,
    # I do not know this url in production:
    url: 'http://example.com/another-script-from-the-remote-server.js'  
  )
  .success(executeLater)
)

Things to avoid

I would hate to use constructions like a couple of setTimout calls until a certain object is defined an the execute the executeLater() method.

What I need: A executed callback

It would be perfect to use a kind of executed callback rather than the success callback of the ajax method. But, so far, I haven't found this callback.

# coffee script

executeLater = ->
  # this bit of code needs to run after the remote code has been executed.
  console.log("yehaa!")

$.ajax(
  dataType: 'script',
  url: 'http://example.com/script-from-other-server.js',
  executed: executeLater  # <---- I NEED A CALLBACK LIKE THIS
)

Any clues?

Does anyone know how I can execute the executeLater method after the remote code has been executed? Thanks!

EDIT: Same-origin policy

As adeneo pointed out in the comments section, JavaScript's same-origin policy might be the problem.

The script, which I load with an ajax or getScript call is not allowed to load and execute another script from a remote server in order to prevent a malicious script from "calling home".

This is supported by the following experiment:

This doesn't work:

<html><head>
  <script language="javascript" src="http://code.jquery.com/jquery-1.10.2.js"></script>
  <script language="javascript">
    jQuery.getScript("http://example.com/script-from-other-server.js")
  </script>
</head><body></body></html>

This works:

According to this stackexchange answer, the same-origin policy allows remote scripts that are loaded by an html <script> tag to load other remote scripts via ajax.

<html><head>
  <script language="javascript" src="http://code.jquery.com/jquery-1.10.2.js"></script>
  <script language="javascript" src="http://example.com/script-from-other-server.js"></script>
</head><body></body></html>

The question remains: Is there a good way to do it with an ajax call, or, do I have to "prove" that I "own this code" by inserting a <script> tag in to the html document?

like image 282
fiedl Avatar asked Sep 12 '13 22:09

fiedl


1 Answers

Background

adeneo's suggestion to consider JavaScripts Same-Origin Policy (see comments to the question) did solve my problem.

Where the question assumes that the success callback is called before the requested script is fully executed, the real problem is, that the requested script does request another script, as stated in the question:

Maybe important: The remote code does contain another getScript(...) call. And I have to wait for this code to be executed as well. I cannot simply load this second script from my code, because its source is dynamic (i.e. depends on some results of the remote script).

When the requested script is loaded dynamically, this second getScript call is prevented by JavaScript's same-origin policy.

Solution 1: Include the script in the html code

If one has access to the html files, one can add a script tag with the remote script as src. Thereby, one "proves" that one really wants to load this remote script and javascript will execute the remote getScript call.

<html><head>
  ...
  <script language="javascript" src="http://code.jquery.com/jquery-1.10.2.js"></script>
  <script language="javascript" src="http://example.com/script-from-other-server.js"></script>
</head><body></body></html>

In order to execute the executeLater code, one can simply use a ready callback:

# coffee script

executeLater = ->
  # this bit of code needs to run after the remote code has been executed.
  console.log("yehaa!")

$(document).ready(executeLater)

Solution 2: Circumvent some-origin policy

This is not recommended, but possible. There is a popular stack overflow question how to get around the same-origin policy:

Ways to circumvent the same-origin policy

Solution 3: Really wait for the script to be executed

If, in addition to the same-origin policy, the remote script really takes so long to be executed that the local script has to wait for it, one can use Ahmed Nuaman's iframe solution:

https://stackoverflow.com/a/18793000/2066546

like image 143
fiedl Avatar answered Sep 28 '22 09:09

fiedl