他の人の JavaScript コードを参照するときに同様のキュー関数を見たことがありますが、これは関数が順番に呼び出されることを保証するためのものであることがわかりました。この記事ではJavaScriptのキュー関数と非同期実行方法を中心に紹介していきますので、お役に立てれば幸いです。
順番に呼び出す必要があるいくつかの関数 fn1、fn2、fn3 があるとします。もちろん、最も簡単な方法は次のとおりです。
fn1();fn2();fn3();
しかし、これらの関数は実行時に 1 つずつ追加されることがあり、いつ追加されるかはわかりません。このとき、関数を追加するときに、配列から関数を 1 つずつ取り出して順番に呼び出すことができます。このように、関数に名前があるかどうかは関係ありません。名前を付けるだけで、匿名関数を渡すこともできます。テストしてみましょう:
var stack = [];// 执行其他操作,定义fn1stack.push(fn1);// 执行其他操作,定义fn2、fn3stack.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() }); // 输出'第一个调用'、'第三个调用'、'第二个调用'
function fn2() { setTimeout(function() { fn2Timeout(); fn3(); }, 0); }
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 は次のように使用されます:
var index = 0; function next() { var fn = stack[index]; index = index + 1; // 其实也可以用shift 把fn 拿出来 if (typeof fn === 'function') fn(); }next通过stack[index]去获取stack中的函数,每调用next一次index会加1,从而达到取出下一个函数的目的。
すべての関数は next を呼び出す必要があります。関数内に記述されていない場合、プログラムは関数の実行後に続行するメカニズムなしで直接終了します。
関数キューの実装を理解した後は、次のインタビューの質問を解決できるはずです:
var stack = []; // 定义index 和nextfunction 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,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。 现在stack.forEach一行已经删掉了,我们自行调用一次next,next会找出stack中的第一个函数fn1执行,fn1 里调用next,去找出下一个函数fn2并执行,fn2里再调用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 */// 以此类推。 Node.js 中大名鼎鼎的connect框架正是这样实现中间件队列的。有兴趣可以去看看它的源码或者这篇解读《何为 connect 中间件》。
redux と koa は、異なる実装を通じて、関数の途中に next を配置し、後続の関数を実行し、その後戻って next の下のコードを実行することができます。 、とても賢いですね。時間があるときにまた書きます。
関連する推奨事項:
イベントのバインドの問題を解決するために、カプセル化された JavaScript イベント キュー関数コードを共有する私自身のカプセル化された JavaScript イベント キュー関数 version_javascript スキルjquery キュー関数の使用例_jquery以上がJavaScriptのキュー関数と非同期実行について詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。