ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript関数の非同期実行実装コードの詳細説明

JavaScript関数の非同期実行実装コードの詳細説明

伊谢尔伦
伊谢尔伦オリジナル
2017-07-22 10:56:201522ブラウズ

順番に呼び出す必要があるいくつかの関数 fn1、fn2、fn3 があるとします。もちろん、最も簡単な方法は次のとおりです。ただし、これらの関数は実行時に 1 つずつ追加され、いつ呼び出されるかわからない場合があります。現時点ではどのような関数があるのか​​わかりませんが、関数を追加するときに、必要に応じて配列から関数を 1 つずつ取り出して、配列に追加することができます。順番に呼び出します:


fn1();
fn2();
fn3();

このような関数には名前がなくても問題ありません。匿名関数を直接渡すことができます。テストしてみましょう:


var stack = [];
// 执行其他操作,定义fn1
stack.push(fn1);
// 执行其他操作,定义fn2、fn3
stack.push(fn2, fn3);
// 调用的时候
stack.forEach(function(fn) { fn() });

この実装は今のところうまく機能していますが、非同期関数の呼び出しという 1 つの状況を無視しました。非同期は JavaScript では避けられないトピックですが、ここでは JavaScript の非同期に関連するさまざまな用語や概念については説明しません (有名な解説など)。次のコードが 1、3、2 を出力することがわかっている場合は、読み続けてください:


var stack = [];
function fn1() {
  console.log('第一个调用');
}
stack.push(fn1);

function fn2() {
  console.log('第二个调用');
}
stack.push(fn2, function() { console.log('第三个调用') });

stack.forEach(function(fn) { fn() }); // 按顺序输出'第一个调用'、'第二个调用'、'第三个调用'

同様の非同期関数である関数がスタック キューにある場合、実装は台無しになります。 :


console.log(1);

setTimeout(function() {
  console.log(2);
}, 0);

console.log(3);

問題は明らかです。fn2 は確かに順番に呼び出されますが、setTimeout の関数 fn2Timeout() { console.log('Second call') } は (タイムアウトが に設定されていても) すぐには実行されません。 0) ; fn2 が呼び出された直後に戻り、fn3 が実行された後、実際に fn2Timeout の番になります。

どうやって解決しますか?ここで重要なのは fn2Timeout です。fn3 を呼び出す前に、実際に実行されるまで待つ必要があります:


var stack = [];

function fn1() { console.log('第一个调用') };
stack.push(fn1);

function fn2() {
  setTimeout(function fn2Timeout() {
     console.log('第二个调用');
  }, 0);
}
stack.push(fn2, function() { console.log('第三个调用') });

stack.forEach(function(fn) { fn() }); // 输出'第一个调用'、'第三个调用'、'第二个调用'

しかし、そうすることは、元の fn2Timeout を削除して置き換えることと同じです。 1 つの New 関数を使用して、元の fn2Timeout と fn3 を挿入します。元の機能を動的に変更するこの方法には、モンキー パッチと呼ばれる特別な用語があります。当社のプログラマーの信条によれば、「必ず実行できる」ですが、書くのは少しぎこちなく、自分自身で取り組むのは簡単です。もっと良い方法はありますか?

一歩下がって、fn3 を実行する前に fn2Timeout が完全に実行されるのを待つ必要はなく、代わりに fn2Timeout 関数本体の最後の行で呼び出します。


function fn2() {
  setTimeout(function() {
    fn2Timeout();
    fn3();
  }, 0);
}

これは見た目が良いですが、 fn2 の定義 fn3 はありません。この fn3 はどこから来たのでしょうか?

fn3 を fn2 で呼び出す必要があるため、stack.forEach を通じて fn3 を呼び出すことができません。そうしないと、fn3 が 2 回呼び出されることになります。

fn3 を fn2 に書き込むことはできません。代わりに、 fn2Timeout の終了時にスタック内で fn2 の次の関数を見つけて、次の関数を呼び出すだけで済みます。

function fn2() {
  setTimeout(function fn2Timeout() {
    console.log('第二个调用');
    fn3();    // 注{1}
  }, 0);
}

この次の関数は、スタック内の次の関数を見つけて実行する役割を果たします。次に next を実装しましょう:



function fn2() {
  setTimeout(function fn2Timeout() {
    console.log('第二个调用');
    next();
  }, 0);
}

next は stack[index] を使用してスタック内の関数を取得します。 next を取り出すという目的を達成するために、next が呼び出されるたびにインデックスが 1 ずつ増加します。関数。

Next は次のように使用されます:


var index = 0;

function next() {
  var fn = stack[index];
  index = index + 1; // 其实也可以用shift 把fn 拿出来
  if (typeof fn === 'function') fn();
}

stack.forEach 行が削除されたので、Next は実行するスタック内の最初の関数 fn1 を見つけて、fn1 で next を呼び出します。 find out 次の関数 fn2 が実行され、次に fn2 で next が呼び出され、以下同様に続きます。
すべての関数は次に呼び出す必要があります。特定の関数に記述されていない場合、プログラムは関数を実行した後、続行するメカニズムが存在せずにすぐに終了します。

この関数キューの実装を理解すると、次のインタビューの質問を解決できるはずです:



var stack = [];

// 定义index 和next

function fn1() {
  console.log('第一个调用');
  next(); // stack 中每一个函数都必须调用`next`
};
stack.push(fn1);

function fn2() {
  setTimeout(function fn2Timeout() {
     console.log('第二个调用');
     next(); // 调用`next`
  }, 0);
}
stack.push(fn2, function() {
  console.log('第三个调用');
  next(); // 最后一个可以不调用,调用也没用。
});

next(); // 调用next,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。

Node.js の有名な接続フレームワークは、この方法でミドルウェア キューを実装します。


注意して見ると、当面はこの next を関数の最後にのみ配置できることがわかりますが、これを中間に配置しても、元の問題が引き続き発生します。

redux と koa は、関数の途中に配置され、後続の関数を実行した後、戻って次のコードを実行します。時間があるときにまた書きます。

以上がJavaScript関数の非同期実行実装コードの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。