web-dev-qa-db-ja.com

jQuery / Javascript-関数を続行する前に、操作されたDOMが更新されるのを待つ方法

私がやろうとしていることは、CPUを多用するスクリプトを実行する前に、単純なdivを「Processing ...」と更新して(実行に3〜12秒かかり、AJAXがない)、divを更新して「Finished! 」それが終わったら。

私が見ているのは、divが「Processing ...」で更新されないことです。そのコマンドの直後にブレークポイントを設定すると、divテキストが更新されるため、構文が正しいことがわかります。 IE9、FF6、Chrome13で同じ動作。

JQueryをバイパスして基本的な生のJavascriptを使用している場合でも、同じ問題が発生します。

これは簡単な答えがあると思います。ただし、jQuery .html()および.text()にはコールバックフックがないため、これはオプションではありません。また、アニメーション化されていないため、操作する.queueはありません。

5秒の高CPU機能を備えたjQuery実装とJavascript実装の両方を示す以下で準備したサンプルコードを使用して、これを自分でテストできます。コードは簡単に追跡できます。ボタンまたはリンクをクリックしても、「Processing ...」が表示されない

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script type="text/javascript">
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    do {start = new Date();} while (end-start > 0);
    document.getElementById('msg').innerHTML = 'Finished JS';   
}
$(function() {
    $('button').click(function(){
        $('div').text('Processing JQ...');  
        start = new Date();
        end = addSecs(start,5);
        do {start = new Date();} while (end-start > 0);
        $('div').text('Finished JQ');   
    });
});
</script>
</head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>
26
pcormier

これを処理に設定してから、setTimeoutを実行して、divが更新されるまでCPUを集中的に使用するタスクが実行されないようにします。

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script>
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    setTimeout(function(){
         start = new Date();
         end = addSecs(start,5);
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished Processing';   
    },10);
}
$(function() {
    $('button').click(doRun);
});    
</script>
    </head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>

必要に応じてsetTimeout遅延を変更できます。遅いマシン/ブラウザでは、より大きくする必要がある場合があります。

編集:

アラートまたは確認ダイアログを使用して、ページの更新を許可することもできます。

document.getElementById('msg').innerHTML = 'Processing JS...';
if ( confirm( "This task may take several seconds. Do you wish to continue?" ) ) {
     // run code here
}
18
Kevin B

5秒間実行され、その間Webブラウザーをフリーズするループがあります。 Webブラウザーはフリーズされているため、レンダリングを実行できません。ループの代わりにsetTimeout()を使用する必要がありますが、ループは、時間がかかるCPU集中型の関数の代わりにすぎないと思いますか? setTimeoutを使用して、関数を実行する前にブラウザーにレンダリングする機会を与えることができます。

jQuery:

$(function() {
    $('button').click(function(){
        (function(cont){
            $('div').text('Processing JQ...');  
            start = new Date();
            end = addSecs(start,5);
            setTimeout(cont, 1);
        })(function(){
            do {start = new Date();} while (end-start > 0);
            $('div').text('Finished JQ');   
        })
    });
});

バニラJS:

document.getElementsByTagName('a')[0].onclick = function(){
    doRun(function(){
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished JS';   
    });
    return false;
};

function doRun(cont){
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    setTimeout(cont, 1);
}

また、常にvarキーワードを使用してすべての変数を宣言し、それらをグローバルスコープに公開しないようにしてください。ここにJSFiddleがあります:

http://jsfiddle.net/Paulpro/ypQ6m/

3
Paulpro

私はjQueryがDOMを操作するのを待って、変更を把握する必要がありました(フォームフィールドをフォームに複数回ロードしてから送信する)。 DOMが大きくなり、挿入に時間がかかりました。 ajaxを使用して変更を保存し、ユーザーが中断したところから続行できるようにする必要があります。

これはNOT WORKを意図したとおりにしました:

_jQuery('.parentEl').prepend('<p>newContent</p>');
doSave(); // wrapping it into timeout was to short as well sometimes
_

.prepend()のようなjQuery関数は、完了時にのみチェーンを継続するため、以下はトリックを行うのようです。

_jQuery('.parentEl').prepend('<p>newContent</p>').queue(function() {
  doSave();
});
_
0
BananaAcid

2019以降、setTimeoutを使用して競合状態を作成する代わりに、フレームをスキップするためにdoublerequesAnimationFrameを使用しています。

....
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    requestAnimationFrame(() =>
    requestAnimationFrame(function(){
         start = new Date();
         end = addSecs(start,5);
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished Processing';   
    }))
}
...
0
Estradiaz