4

Javascript を使用してかなり複雑な計算 (階乗とベッセル関数を含む) を実行する Web アプリを作成しようとしています。IE でスクリプトを実行すると、スクリプトが応答しないか、時間がかかるという警告が表示され、実行を続行するかどうか尋ねられます。これを回避するには、setTimeout または setInterval コマンドを使用して、スクリプトが長時間実行されているかどうかを判断するために IE が使用するカウンターを本質的にリセットできることを読みました。

これを実装しようとしましたが、成功しませんでした。プロファイラーを実行すると、階乗を計算する関数に最も時間がかかるように見えるので、その関数で setTimeout を使用したいと思います。私が現在持っている機能は次のとおりです。

function factorial(x) {
    var buff = 1;

    for (i=x;i>=1;i--) {
        buff = buff * i;
    }

    return buff
}

コードを次のように置き換えてみましたが、正しく機能しません。

function factorial(x) {
    if (x==0) {
        factbuff=1;
    }
    else {
        factbuff = x;
        factidx = x;
        setTimeout('dofact()',50);
    }
    return factbuff
}

function dofact() {
    if (factidx > 1) {
        factidx--;
        factbuff = factbuff * factidx;
    }
}

私が間違っていることと、IEでスクリプトの警告を排除しながら階乗を計算するためにsetTimeout関数を正しく実装する方法を知っている人はいますか?

4

1 に答える 1

0

これは更新であり、以前の回答 (現在は削除されています) のコードが間違っている理由の説明です。

まず、問題をもう一度説明しましょう。「実行時間の長いスクリプト」の警告をトリガーすることなく、階乗関数を IE で実行できるようにします。

これは、以前に提案されたコードです。

BROKEN. DO NOT USE. 

var executions = 0;
function factorial(x) {
    executions++;
    if (x > 1) {
    if (executions % 100 === 0) {
    return (function() {  // NO NO NO
        var y = x;
        setTimeout(function(y) { return y*factorial(y-1); }, 50);
    })();
    } else {
        return x*factorial(x-1);
    }
    } else {
    return 1;
    }
}

では、そのコードの何が問題なのですか?

  1. 階乗関数自体は再帰的です。つまり、関数は自分自身を呼び出します。関数がそれ自体を呼び出すたびに、別のスタック フレームがメモリに取得されます。上記のコードがしようとしているのは、自分自身を 100 回呼び出すことです。ブラウザーが許容できるネストされたスタック フレームの数はわかりませんが、100 は少し高いようです。10個単位でやります。

  2. 上で提案された関数は非同期ではありません。IE の警告を回避するために setTimeout() を使用する場合、関数を非同期にする必要があります。つまり、 のように呼び出すのではなくvar value = func(x);、非同期関数が結果を取得したときに呼び出すコールバックを渡すようにコードを変換する必要があります。

  3. 上記の問題に関連して、提案されたコードでの setTimeout の使用は間違っています。ある場所では、コードはこれを行います:

    return (function() {  // NO NO NO
        var y = x;
        setTimeout(function(y) { return y*factorial(y-1); }, 50);
    })();
    

それは何をしますか?分解してみましょう。匿名機能があります。その関数が呼び出されます (最後の開閉括弧によって)。そして、その呼び出しの値は、主階乗関数によって返されます。アノン関数を呼び出す価値は何ですか? 問題 a: 値を返さない。未定義です。( return ステートメントがない場合、javascript 関数は何を返しますか? を参照してください。 )

それを修正することは、 への呼び出しの値を返すほど簡単ではありませんsetTimeout()。それも間違っています。の戻り値setTimeout()は、 でタイムアウトをクリアするために使用できる識別子ですclearTimeout() setTimeout が呼び出すものによって提供される値ではありません。


わかりました、それを修正する方法は?まず、階乗関数が非同期になることを理解してください。したがって、結果の取得と表示は次のようになります。

function displayFacResult(result) {
    var t2 = document.getElementById('t2');
    t2.value = result;
}

function b1_Click() {
    var t1 = document.getElementById('t1'),
        value = parseInt(t1.value, 10);
    computeFactorialAsynchronously(value, displayFacResult);
}

ボタンのクリックで「compute」が呼び出され、結果で呼び出される関数の名前が渡されます。結果で呼び出される関数は、実際に表示を行います。これは非同期呼び出しパターンです。

さて、計算です。

function computeFactorialAsynchronously(firstX, callback) {
    var batchSize = 3, limit=0, result = 1;
    var doOneFactorialStep = function(x, steps) {
        if (steps) { limit = x - steps; }
        if (x==1) { callback(result); }
        if (x>limit) {
            result *= x;
            doOneFactorialStep(x-1);
        }
        else {
            setTimeout(function () {
                doOneFactorialStep(x, batchSize);
            }, 1);
        }
    };
    doOneFactorialStep(firstX, batchSize);

    // the actual return value of the computation
    // always comes in the callback.
    return null;
}

「チャンク」によって階乗を計算し、各チャンクには N 個の乗算が含まれ、上記の変数「ステップ」で表されます。N (ステップ) の値は、再帰のレベルを決定します。100 は大きすぎる可能性があります。3 はおそらく小さすぎて良好なパフォーマンスを発揮できませんが、非同期性を示しています。

computeFactorialAsynchronously 関数内には、1 つのチャンクを計算してから setTimeout を呼び出して次のチャンクを計算するヘルパー関数があります。現在のチャンクの計算をいつ停止するかを管理するための簡単な計算がいくつかあります。

作業例: http://jsbin.com/episip

ある意味では、非同期モデルへの移行は、計算の結果が関数の結果である、純粋に機能的な比喩から離れます。これは JavaScript で行うことができますが、IE の「実行時間の長いスクリプト」の警告が表示されます。警告を回避するために、非同期に移行します。つまり、"computeFactorial" の戻り値は実際の階乗ではありません。非同期モデルでは、計算関数が計算終了時に呼び出すコールバック関数から、「副作用」を介して結果を取得します。

于 2011-10-05T15:05:10.317 に答える