web-dev-qa-db-ja.com

Javascriptのトライキャッチパフォーマンスとコードのチェック中にエラーが発生しました

さまざまなエラーチェックを実行する代わりに、try-catchブロック内にコードを配置する方が速いでしょうか?

例えば..

function getProjectTask(projectTaskId) {
    if (YAHOO.lang.isUndefined(projectTaskId) || YAHOO.lang.isNull(projectTaskId) && !YAHOO.lang.isNumber(projectTaskId)) {
        return null;
    }

    var projectPhaseId, projectPhaseIndex, projectTaskIndex, projectPhases, projectPhase, projectTask;

    if (!YAHOO.lang.hasOwnProperty(projectTaskPhaseMap, projectTaskId)) {
        return null;
    }

    projectPhaseId = projectTaskPhaseMap[projectTaskId];

    if (YAHOO.lang.isUndefined(projectPhaseId) || YAHOO.lang.isNull(projectPhaseId) || !YAHOO.lang.hasOwnProperty(scheduleData.ProjectPhasesMap, projectPhaseId)) {
        return null;
    }

    projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId];
    if (YAHOO.lang.isUndefined(projectPhaseIndex) || YAHOO.lang.isNull(projectPhaseIndex) || !YAHOO.lang.hasOwnProperty(scheduleData.ProjectPhases[projectPhaseIndex])) {
        return null;
    }
    projectPhase = scheduleData.ProjectPhases[projectPhaseIndex];

    if (!YAHOO.lang.hasOwnProperty(projectPhase.ProjectTasksMap, projectTaskId)) {
        return null;
    }

    projectTaskIndex = projectPhase.ProjectTasksMap[projectTaskId];

    if (YAHOO.lang.isUndefined(projectTaskIndex) || YAHOO.lang.isNull(projectTaskIndex)) {
        return null;
    }

    projectTask = scheduleData.ProjectTasks[projectTaskIndex];
}

[〜#〜] vs [〜#〜]

function getProjectTask(projectTaskId) {
    try {
        projectPhaseId = projectTaskPhaseMap[projectTaskId];
        projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId];
        projectPhase = scheduleData.ProjectPhases[projectPhaseIndex];
        projectTaskIndex = projectPhase.ProjectTasksMap[projectTaskId];
        projectTask = scheduleData.ProjectTasks[projectTaskIndex];

    }
    catch (e) {
        return null;
    }
}

私の質問が理にかなっていることを願っています。明確にさせていただきます。ありがとうございました!

32
Abe

"プログラムは、人々が読むために、そして偶然にマシンが実行するためにのみ書かれなければなりません。"

Abelson&Sussman、SICP、初版の序文

常に読み取り可能コードを目指します。覚えておくべき重要なことは次のとおりです。

パフォーマンスが重要な関数やループでのtry-catchは避けてください

それ以外の場所では、それほど害はありません。それらを賢く使用し、控えめに使用してください。補足として、古いブラウザをサポートしたい場合は、try-catchがない可能性があります。

しかし、私が見ているように、エラーチェックのためにいくつかの関数を明らかに誤用しています。複雑なチェックを行う代わりに、使用する直前に目的のオブジェクトとオブジェクトのプロパティをテストできます。そして:

if (YAHOO.lang.isUndefined(projectPhaseId) || YAHOO.lang.isNull(projectPhaseId))

次のように書くことができます

if (projectPhaseId != null)

たとえば...したがって、上記の例は、trycatchがなくてもかなり読みやすくなります。あなたは誤用 YUIを少ししているようです。

私は賭けますこれはうまくいきます予想通り:

function getProjectTask(projectTaskId) {

   var projectPhaseId    = projectTaskPhaseMap[projectTaskId],
       projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId],
       projectPhase      = scheduleData.ProjectPhases[projectPhaseIndex];

  if (projectPhase == null) return null; // projectPhase would break the chain

  var projectTaskIndex  = projectPhase.ProjectTasksMap[projectTaskId],
      projectTask       = scheduleData.ProjectTasks[projectTaskIndex];

   return projectTask || null; // end of the dependency chain

}

どのようにクールそれは何ですか? :)

46
gblazex

なぜ議論の事実の根拠を持っていないのですか?次のコードは、パフォーマンスへの影響を示しています。

var Speedy = function() {
    this.init();
};
Speedy.prototype = {
    init: function() {
        var i, t1;
        this.sumWith = 0;
        this.sumWithout = 0;
        this.countWith = 0;
        this.countWithout = 0;
        for (i = 0; i < 5; i++) {
            t1 = this.getTime();
            console.log("Using Try/Catch, Trial #" + (i + 1) );
                        console.log("started " + t1 );
            this.goTry(t1);
            this.countWith++;
        }
        for (i = 0; i < 5; i++) {
            t1 = this.getTime();
            console.log("W/out Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1 );
            this.goAlone(t1);
            this.countWithout++;
        }
        for (i = 5; i < 10; i++) {
            t1 = this.getTime();
            console.log("Using Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1);
            this.goTry(t1);
            this.countWith++;
        }
        for (i = 5; i < 10; i++) {
            t1 = this.getTime();
            console.log("W/out Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1);
            this.goAlone(t1);
            this.countWithout++;
        }
        console.log("---------------------------------------");
        console.log("Average time (ms) USING Try/Catch: " + this.sumWith / this.countWith + " ms");
        console.log("Average time (ms) W/OUT Try/Catch: " + this.sumWithout / this.countWithout + " ms");
        console.log("---------------------------------------");
    },

    getTime: function() {
        return new Date();
    },

    done: function(t1, wasTry) {
        var t2 = this.getTime();
        var td = t2 - t1;
        console.log("ended.....: " + t2);
        console.log("diff......: " + td);
        if (wasTry) {
            this.sumWith += td;
        }
        else {
            this.sumWithout += td;
        }
    },

    goTry: function(t1) {
        try {
            var counter = 0;
            for (var i = 0; i < 999999; i++) {
                counter++;
            }
            this.done(t1, true);
        }
        catch (err) {
            console.error(err);
        }
    },

    goAlone: function(t1) {
        var counter = 0;
        for (var i = 0; i < 999999; i++) {
            counter++;
        }
        this.done(t1, false);
    }
};

var s = new Speedy();

このJSFiddleは、firebug liteのコンソールに出力を表示します: http://jsfiddle.net/Mct5N/

28
Jon J

ドグマを脇に置き、現時点ではここでの答えに満足していない...

コードが例外をスローすることがめったにない場合は、例外をキャッチしたり防止したりするための追加のオーバーヘッドがないため、違反者の周りにtry-catchを配置するとうまく機能します。

コードが予測できないデータまたはそれに類似したシナリオに基づいて例外をスローするのが一般的である場合、ガードメソッドを配置すると、パフォーマンスが大幅に向上し、例外が頻繁に発生する場合は最大20倍になります。

アプローチをアドバイスする場合、深いネストがない場合は、可能であれば単純なガード演算子を使用してください。より深いネストの場合は、必要に応じてトラバースできるガード方式を使用してください。

これは私がこれに基づいた私自身のいくつかのテストです。

http://jsfiddle.net/92cp97pc/6/

シナリオでは、以下を比較していますが、ループしています。

var a;

// scenario 1 (always throws/catches)
try { a.b.c.d; }
catch(ex) { }

// scenario 2 (about 20 times faster than scenario 1)
guard(a, 'b', 'c', 'd');

// now no exceptions will occur
a = { b: { c: { d: true } } };

// scenario 3 (too fast to measure)
try { a.b.c.d; }
catch(ex) { }

// scenario 4 (.04 times slower than scenario 3)
guard(a, 'b', 'c', 'd');
4
Jason Sebring

確かに、それはよりコンパクトなコードになりますが、それはあなたのデバッグ能力を減らし、そして優雅なエラー回復、または有用なエラーメッセージを追加することをはるかに難しくします。

3
Brock Adams

状況によります。 galambalazsが言及しているように、読みやすさは重要です。考えてみましょう:

function getCustomer (id) {
  if (typeof data!='undefined' && data.stores && data.stores.customers 
      && typeof data.stores.customers.getById=='function') {
    return data.stores.customers.getById(id);
  } else {
    return null;
  }
}

に比べ:

function getCustomer (id) {
  try {return data.stores.customers.getById(id);} catch (e) { return null; }
}

2番目の方がはるかに読みやすいと思います。あなたはグーグルのAPIやツイッターのフィードのようなものからこのようにデータを取り戻す傾向があります(通常は深くネストされたメソッドではありませんが、それはデモンストレーションのためにここにあります)。

もちろん、パフォーマンスも重要ですが、最近のjavascriptエンジンは十分に高速であるため、10ミリ秒ごとにgetCustomerを呼び出さない限り、誰も違いに気付かない可能性があります。

1
Dagg Nabbit

これはブラウザによっても異なることに注意してください。ただし、全体として、try/catchブロックを使用した場合の重大なパフォーマンスペナルティについては何も読んでいません。ただし、問題が失敗した理由がわからないため、これらを使用することは必ずしも良い習慣ではありません。

これは、JavaScriptのパフォーマンスに関する考慮事項の興味深いスライドショーです。スライド76では、try/catchブロックとパフォーマンスへの影響について説明しています。 http://www.slideshare.net/madrobby/extreme-javascript-performance

0
spinon

パフォーマンスに関するtry-catchは、チェックの場合よりも20〜50%遅くなります( https://jsperf.com/throw-catch-vs-if-check/1 )。

したがって、まれな使用法では、あまり違いはありません。頻繁に使用する場合は、多少の違いが生じる可能性があります。

ただし、読みやすさが大幅に低下する場合を除いて、ifチェックで実行できる場合は、try-catchを使用するのは悪い習慣だと思います。

0
Anil Tallam