ホームページ  >  記事  >  ウェブフロントエンド  >  JSキュー関数の詳細説明と非同期実行例

JSキュー関数の詳細説明と非同期実行例

怪我咯
怪我咯オリジナル
2017-07-07 14:57:151370ブラウズ

この記事では主に JavaScript キュー関数と非同期実行に関する情報を詳しく紹介します。興味のある方は参考にしてください。

編集者注: 他の人の JavaScript コードをレビューしているときに、同様のキューを見たことがあります。関数を理解していますが、これは関数が順番に呼び出されることを保証するためであることがわかりました。この記事を読んで、非同期実行などにも使えることが分かりました。

順番に呼び出す必要があるいくつかの関数 fn1、fn2、fn3 があるとします。もちろん、最も簡単な方法は次のとおりです。

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

しかし、これらの関数は実行時に 1 つずつ追加されることがあり、いつ追加されるかはわかりません。このとき、関数を追加するときに、配列から関数を 1 つずつ取り出して順番に呼び出すことができます。このように、関数に名前があってもなくても、そのまま

匿名関数

を渡すこともできます。テストしてみましょう:

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('2 回目の呼び出し') } はすぐには実行されません (タイムアウトが 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 を削除して新しい関数に置き換えることと同じです。をクリックし、元の 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 が呼び出されるたびにインデックスが 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 を呼び出します。 fn2 を実行し、fn2 で next を呼び出します。

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

これは、有名な connectframeworkミドルウェアqueue を実装する方法です。ご興味がございましたら、そのソース コードまたはこの説明「接続ミドルウェアとは」をご覧ください。
注意して見ると、この next は関数の最後にのみ配置できることがわかりますが、これが中間に配置された場合でも、元の問題が引き続き発生します:

// 实现一个LazyMan,可以按照以下方式调用:
LazyMan(“Hank”)
/* 输出: 
Hi! This is Hank!
*/

LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
/* 输出: 
Hi! This is Hank!
// 等待10秒..
Wake up after 10
Eat dinner~
*/

LazyMan(“Hank”).eat(“dinner”).eat(“supper”)
/* 输出: 
Hi This is Hank!
Eat dinner~
Eat supper~
*/

LazyMan(“Hank”).sleepFirst(5).eat(“supper”)
/* 等待5秒,输出
Wake up after 5
Hi This is Hank!
Eat supper
*/

// 以此类推。

redux と koa は next in に配置できます。さまざまな実装による関数 途中で、次の関数を実行した後、戻って以下のコードを実行しますが、これは非常に賢い機能です。時間があるときにまた書きます。

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

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