3

我制作了一个 jsbin 来显示问题: http ://jsbin.com/dexeqiz/edit?html,js,output

有这个html:

<div id='log'></div>
<div id='scripts'></div>

和js:

$.get('...', function(){
    $('#scripts')
        .append("<script>$(function(){$('#log').append('<p>3</p>');});<\/script>");
    $('#log').append('<p>1</p>');
    $('#log').append('<p>2</p>');
});

在 jquery 1 和 2 中

它将在#log 中呈现:
3
1
2

但在 jquery 3 中它将呈现
1
2
3

(所以只有在整个 ajax 处理程序完成后才添加 3)

这是一个问题,因为有时我的代码期望在调用下一行之前执行之前附加在行中的代码


现在我唯一的解决方法是将代码.append(newhtml)放在 a之后setTimeout,但我不希望这样做,因为它对用户来说看起来有点慢。我宁愿有类似的东西$.when(append).done(function(){code})

更新: 似乎发生这种情况是因为从 jQuery 3 脚本开始,用于文档就绪$(function(){});加载异步(https://github.com/jquery/jquery/issues/1895),这是我目前的解决方案:http: //jsbin.com/ xayitec/edit?html,js,输出

4

1 回答 1

1

After all fiddling it stands out: there is no real solution for this issue; at least none that is not very hackish or without changing the whole setup/workflow.

For the sake of completeness I leave my "answers" as they are (see everything below "LOG") and add some background information.

  • https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous

    The document-ready processing in jQuery has been powered by the jQuery.Deferred implementation since jQuery 1.6. As part of jQuery 3.0's alignment with the Promises/A+ standard, document-ready handlers are called asynchronously even if the document is currently ready at the point where the handler is added. This provides a consistent code execution order that is independent of whether the document is ready or not.

  • https://github.com/jquery/jquery/issues/3773#issuecomment-325999054

    Since you're wrapping code in your script tag in $, you can also wrap the other lines:

    $.get('...', function(){ $('#somediv') .append( "somehtml<script>$(function(){$('#log').append('<p>3</p>');});\/script>" ); $(function(){ $('#log').append('<p>1</p>'); $('#log').append('<p>2</p>'); }); });

    I'd try to not rely on such order too much, though, such code can get quite brittle. I think we technically don't guarantee that order is preserved but that's the case right now.

    Often a better solution is to put scripts at the end of body and then wrapping functions in $ is not necessary.

  • https://github.com/jquery/jquery/issues/1895
  • https://github.com/jquery/jquery/pull/2126



One more idea - very hackish

You can search for all scripts in the string that will be appended. Then search for all "$( function(){...} )" occurrences with regex then insert a function like "$( function(){...;executionHandler()} )" that will count down until all these constructs are resolved and then set an outer promise as resovled. But this would be quite hackish as I said at the beginning and also might be quite error prone.



LOG

Version 1, 2, 2.1 and 3 are all tested with jQuery versions 1.12.4, 2.2.4 and 3.2.1 and should work fine with all versions from jQuery 1.8 and above.

Version 1

$.get('...', function(){

    // make shure that $Deferred is in scope of all involved functions
    window.$Deferred = $.Deferred(); // get jQuery deferred object

    $('#scripts').append(
        "hi<script>$(function(){$('#log').append('<p>3</p>');});<\/script>" + // append your common stuff
        "<script>$(function(){$Deferred.resolve();})<\/script>" // add this line to resolve window.$Deferred
    );

    $Deferred.always(function() {
        // execute code after $Deferred was resolved or rejected
        $('#log').append('<p>1</p>');
        $('#log').append('<p>2</p>');
    });

});

Version 2

was kicked


Version 3

// $.get returns a jqXHR object which is a promise-compatible object
$.when($.get('...', function() {
    $('#scripts').append(
        "hi<script>$(function(){$('#log').append('<p>3</p>');});<\/script>"
    )
})).always(function( data, textStatus, jqXHR|errorThrown ) {
    // execute code when $.get is done or fails
    $('#log').append('<p>1</p>');
    $('#log').append('<p>2</p>');
});

Or alternatively

$.when($.get('...', function() {
    $('#scripts').append(
        "hi<script>$(function(){$('#log').append('<p>3</p>');});<\/script>"
    )
})).then(
    function( data, textStatus, jqXHR ) {
        // success callback
    },
    function( jqXHR, textStatus, errorThrown ) {
        // fail callback
    }
);

Details


References

于 2017-08-30T00:36:22.577 回答