Background
我正在通过jQuery的.ajax(...)
调用从另一台服务器(跨域)加载和执行脚本。
其他服务器的代码之后,需要执行一些代码,否则某些对象是"未定义的"。
可能很重要:远程代码确实包含另一个getScript(...)
调用。我还必须等待此代码执行。我不能简单地从我的代码中加载第二个脚本,因为它的来源是动态的(即取决于远程脚本的某些结果)。
不起作用:success
回调
显然,success
回调是在远程代码被下载之后,但在执行远程代码之前调用的。
# 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.
不起作用:async: false
显然,对于跨域请求,async
属性被忽略,如jQuery文档中所述:http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
此外,我想避免async: false
设置,因为据说它会阻止浏览器。
# 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)
不起作用:$.when(...).then(...)
显然,使用 jQuery 的 when-then 机制,then
代码是在执行 when 块之前执行的。
# 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)
编辑:确实有效,但无法使用:ajax
两个脚本
正如我上面在"后台"部分所说的那样,我无法在生产中执行此操作,但是如果我将所有情况减少到一种情况并在我自己的脚本中加载第二个脚本(通常由远程脚本执行),则一切正常。
# 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)
)
要避免的事情
我讨厌使用像几个setTimout
调用这样的结构,直到某个对象被定义为执行executeLater()
方法。
我需要什么:executed
回调
使用一种executed
回调而不是ajax
方法的success
回调将是完美的。但是,到目前为止,我还没有找到这个回调。
# 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
)
有什么线索吗?
有谁知道在执行远程代码后如何执行 executeLater
方法?谢谢!
编辑:同源策略
正如 adeneo 在评论部分指出的那样,JavaScript 的同源策略可能是问题所在。
我使用ajax
或getScript
调用加载的脚本不允许从远程服务器加载和执行另一个脚本,以防止恶意脚本"呼叫总部"。
以下实验支持这一点:
这不起作用:
<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>
这有效:
根据这个 stackexchange 答案,同源策略允许由 html <script>
标签加载的远程脚本通过 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>
问题仍然存在:是否有一种好方法可以使用 ajax 调用来做到这一点,或者,我是否必须通过在 html 文档中插入 <script>
标签来"证明"我"拥有此代码"?
背景
adeneo 建议考虑 JavaScripts 同源策略(请参阅对问题的评论)确实解决了我的问题。
如果问题假设在请求的脚本完全执行之前调用了success
回调,则真正的问题是,请求的脚本确实请求了另一个脚本,如问题中所述:
可能很重要:远程代码确实包含另一个
getScript(...)
调用。我还必须等待此代码执行。我不能简单地从我的代码中加载第二个脚本,因为它的来源是动态的(即取决于远程脚本的某些结果)。
当请求的脚本动态加载时,JavaScript 的同源策略会阻止第二次getScript
调用。
解决方案 1:在 html 代码中包含脚本
如果可以访问 html 文件,则可以添加带有远程脚本的脚本标记作为src
。因此,一个人"证明"一个人真的想加载这个远程脚本,javascript 将执行远程getScript
调用。
<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>
为了执行executeLater
代码,可以简单地使用ready
回调:
# coffee script
executeLater = ->
# this bit of code needs to run after the remote code has been executed.
console.log("yehaa!")
$(document).ready(executeLater)
解决方案 2:规避某个源策略
不建议这样做,但可以这样做。有一个流行的堆栈溢出问题,如何绕过同源策略:
规避同源策略的方法
解决方案 3:真正等待脚本执行
如果除了同源策略之外,远程脚本确实需要很长时间才能执行,以至于本地脚本必须等待它,则可以使用 Ahmed Nuaman 的 iframe 解决方案:
https://stackoverflow.com/a/18793000/2066546
有点讨厌,但你试过使用iframe
吗?这个想法很简单:
- 有一个表格,其中包含适合您尝试发出的请求的
action
和method
。 - 将表单的
target
(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) 设置为指向页面上的iframe
,可以隐藏此iframe
。 - 聆听
iframe
的onload
,当iframe
加载后,您可以执行代码。
所以这里有一个小例子(使用jQuery):
<form id="xform" action="http://foo.bar/foo/bar" method="get" target="xiframe">
<input type="text" name="foo" value="bar" />
</form>
<iframe name="xiframe" id="xiframe"></iframe>
<script>
var form = $('#xform'),
iframe = $('#xiframe');
iframe.load(function () {
// run your stuff here
});
form.submit();
</script>