2

今のところ、try/catchブロックがまったく機能するかどうかは私には謎です。私はそれらをコードの周りに設定しましたが、コード内の何かが「非同期」であったため、OSレベルで別のスレッド/プロセスにフォークされたと言うための派手な方法のように見えるため、その中で発生した場合、try/catchは無視されますコード。

私はそれで大丈夫です、私はこれの何らかの兆候があるかどうか知りたいだけですか?慣例により、呼び出しがコールバックを要求する場合、それはasychであり、そうでない場合はそうではないことを理解するように与えられています。コールバックがasychを意味する理由はわかりますが、その逆が常に当てはまるとは限らないのではないかと思います。新しいコールスタックに読み込まれ、要求しないtry/catchでコールを囲むことを妨げるものは何もありません。コールバック。これは私には本当に厄介なように思えます。可能であれば、キャッチされなかったすべての例外が処理されるデフォルトのコールバックを使用するよりも、try/catchをもう少し制御したいと思います。

  1. コードが現在のスタックを離れるタイミングを教えてくれるセマンティクスはありますか?

更新:ここに例があります:

var UserSchema = new mongoose.Schema({
  email: {type: String, unique: true},
  password: String,
  firstName: String,
  lastName: String,
  created_at: {type: Date, default: Date.now},
  updated_at: Date
});


var User = mongoose.model('User', UserSchema);

var users = [
  {email: 'foo@bar.com', firstName: 'Justin', lastName: 'Jones'},
  {email: 'foo@bar.com', firstName: 'Justin', lastName: 'Jones'}
];


users.forEach(function(user) {

          var newUser = new User(user);
          newUser.save(function(err, obj) {

            if (!err) console.log("Saved: ", obj.email);

          });

});

上記のコードを考えると、別の呼び出しスタックで発生するため、save()内で例外をキャッチする方法はありません。save()を呼び出したときに何が起こっているのかを外部から知る方法はありますか?

更新:これにハンドラーを使用するように私に言っている人は、おそらくこれを読むべきですか?スレッドのようにしか機能しないため、「スレッドの実行」に含まれない例外を処理しないことを明確に示唆しています。

4

4 に答える 4

4

「非同期」は、 「別のスレッド/プロセスに分岐したという派手な言い方」ではありません。

JavaScript はシングルスレッドです。話の終わり。言語レベルでの分岐はありません。

「非同期」とは、まさにその意味です。実行の順序はコードの順序ではありません。コードの一部 (コールバック関数) は、特定のイベントが発生した時点で実行されます。これは、イベント ベースのプログラミング モデルです。

次の簡単な例を考えてみましょう。

function hello () { alert("Hello!"); }

setTimeout(hello, 2000);

これは最も基本的な形式の非同期コールバックです。コールバック ハンドラー (関数hello) とイベント ジェネレーター (この例では時間ベースのイベント ジェネレーター) があります。

イベントが発生すると (2 秒が経過すると)、コールバック ハンドラが呼び出されます。

修正のために:

function hello() { alert(foo.bar); }

try {    
  setTimeout(hello, 2000);
} catch (ex) {
  alert(ex.message);
}

の周りに try-catch ブロックを導入しsetTimeoutます。callbackの登録を保護するだけです。ほとんどの場合、このステップは成功するため、try-catch ブロックは何もしません。コールバック自体が今から 2 秒後に失敗するという事実は、当然、try-catch ブロックには影響しません。これは、混乱を招く動作です。

次に、別の変更を行います。

function hello() { 
  try {    
    alert(foo.bar); 
  } catch (ex) {
    alert("foo.bar is not defined");
  }
}

setTimeout(hello, 2000);

現在、try-catch ブロックは、実際に失敗する可能性のあるステップを保護しています。これは、エラーが発生する可能性がある場所で try-catch ブロックを使用する必要があることを意味します。一般に、プログラムの大きなセクションをラップするのではありません (これはあなたがしているようです)。

しかし、便利で構成可能な何かを行うために例外を取得するにはどうすればよいでしょうか? 当然のことながら、より多くのコールバックを導入することによって。

function hello(onError) { 
  try {    
    alert(foo.bar); 
  } catch (ex) {
    onError("foo.bar is not defined", ex);
  }
}

function errorHandler(customMessage, exception) {
  alert(customMessage);
  // or do something with exception 
}

setTimeout(function () {
  hello(errorHandler)
}, 2000);

追加された例に従って:

var saveUsers = function(users, onSuccess, onError) {
  users.forEach(function(user) {
    var newUser = new User(user);

    newUser.save(function(err, obj) {
      if (err) {
        onError(err, obj);
        return false;
      } else {
        onSuccess(obj);
      }
    });
  });
}

var users = [
  {email: 'foo@bar.com', firstName: 'Justin', lastName: 'J'},
  {email: 'foo@bar.com', firstName: 'Justin', lastName: 'J'}
];

saveUsers(
  users, 
  function (user) { console.log("Saved: ", user.email); },
  function (err, user) { console.log("Could not save: ", user.email); }
});
于 2012-04-24T19:47:07.637 に答える
2

セマンティックは文字通り、呼び出した関数の実行が終了したときです。I/O プロセスを生成してイベントを登録する場合、try-catch暗黙の Javascript イベント ループを介して別のループで実行されるため、ブロックはそれを取り囲みません。

実行中の関数にコールバック パラメーターが存在するかどうかは、関数によって開始された作業が別の場所でイベントを発生させるかどうかには関係ありません。EventEmitterベースのオブジェクトはハンドラーを.on('eventName', functionName)メカニズムに登録するため、複数のイベントと複数のハンドラーが同じ「作業」にアクセスできますが、それはすべて、コールバックを受け取らない関数によって開始されます。ArrayオブジェクトのforEachメソッドはコールバックを受け取り、同期しています。

簡単に言えば、イベント ループ バリアを超えて Javascript 例外がスローされることはありません。Javascript 側のコードのみが可能です。そのtry-catchため、必要に応じて、ブロックをその側に配置します。関数がエラーをスローする可能性がある場合は非同期コードを呼び出す関数に、エラーをスローする可能性のある何かを呼び出す場合はコールバック関数自体に配置します。非同期の場合、Javascript の観点からは 2 つの別個のコール スタックであるため、try-catchスコープが異なります。同期の場合は、追加のtry-catchチェック セットが 1 つあるだけで、少なくとも何がエラーをスローしたかをよりよく理解できます。

個人的には、try-catch は Javascript のような言語では機能せず、より Java らしくするために追加されたと思うのでthrow、Node.js を使用するコードを避けるようにしています。(例外は、機能しないライブラリ/オブジェクトの初期構成にのみそれらを使用している場合、または[実行オーバーヘッドのため、私の意見では不十分です] 深い同期コードから抜け出すための内部的な方法として使用している場合です。そして、それを私に公開しません。)

編集: Javascript のコールスタックをよりよく説明するために、スタックの各レベルと時間を示す単純な ASCII 図を次に示します。

== Javascript Event Loop =========================================================
== My Awesome Function ======================================    == My callback ==
    == Call to an async library ====================
        == I/O request and callback registration ===

によってスローされたものMy callbackはすべて、Javascript イベント ループに直接送り返され、process.on('uncaughtException', myFunc)その時点で処理するハンドラーを登録することによってのみキャッチできます。基本的に、独自のコードは確実に を使用できますが、イベント ハンドラーとして直接呼び出す場合は絶対に使用try-catchしないでください。throw

あなたの質問に対する最近の編集に関しては、あなたの問題async.forEachを解決します。配列を渡して反復処理を行い、次に配列内の各項目に対して実行する関数を渡します。次に、"finally" スタイルの関数を渡して、エラーを処理するか、そこからコードを続行します。

于 2012-04-24T19:45:42.377 に答える
1

Nathan: 「コードがいつ現在のスタックを離れるか」という大きな問題には立ち入らないことにします。ただし、ユーザーを救うお手伝いはできます。優れた非同期ライブラリをお勧めします。

function saveUser(user, callback) {
    new User(user).save(callback);
}

async.forEach(users, saveUser, function(err, results) {
    // Respond
});
于 2012-04-24T20:15:25.790 に答える
-1

単にコールバック関数を使用している場合は問題ありません。NodeJS はシングルスレッドであるため、すべてが同じ呼び出しスタックにあります。

しかし!NodeJS が現在のコール スタックを「離れる」場合があることについては、そのとおりです。leave は引用符で囲まれています。これは、実際には現在のコール スタックを離れるのではなく、単にルート コールに戻るためです ( process.nextTick())。次に、次のティックで、「新しい」コール スタックが生成されます。

EventEmitterセマンティックが必要な場合は、 を使用している場所ならどこでも、そのコールバック関数を次のティックに渡しているため、別のコールスタックに「移動」していると言えると思います。(ただし、EventEmitter は現在のティックで実際にイベントをディスパッチするため、完全に正しいわけではありません)。

于 2012-04-24T20:05:47.487 に答える