jsはシングルスレッド言語であるため、非同期タスク(ajax操作など)が発生した場合、非同期の完了を待って実行を継続することはできません。この間、ブラウザーはアイドル状態になります。膨大なリソースの浪費に。
実行スタック
関数が実行されると、ユーザーはマウスを1回クリックし、Ajaxが完了し、画像の読み込みが完了し、その他のイベントが発生します。コールバック関数が指定されている限り、これらのイベントは発生時に実行スタックキューに入り、待機します。読むメインスレッドは、先入れ先出しの原則に従います。
メインスレッド
明確にする必要があるのは、メインスレッドが実行スタックとは異なる概念であるということです。メインスレッドは、実行スタック内のどのイベントが現在実行されているかを指定します。
メインスレッドループ:つまり、メインスレッドは実行スタックからイベントを継続的に読み取り、スタック内のすべての同期コードを実行します。
非同期イベントが発生すると、非同期イベントが結果を返すのを常に待機するわけではありませんが、タスクキューと呼ばれる実行スタックとは別のキューでイベントをハングさせます。
メインスレッドが実行スタック内のすべてのコードを実行した後、メインスレッドはタスクキューにタスクがあるかどうかを確認します。その場合、メインスレッドはタスクキュー内のコールバック関数を順番に実行します。
グローバルスコープでのjsコード、new Promise、Promise.then、Promise.all()。then()、process.nextTickの実行順序
新しいPromiseはすぐに実行されるため、内部のコードは順番に実行されます。対応する順序は次のとおりです。シーケンシャルjsコード->新しいPromise->シーケンシャルjsコード(グローバルjsと新しいPromiseの実行後)->すべてのprocess.nextTick-> Promise()。then()-> Promise.all()。then()
console.log(1);// 顺序-1
Promise.all([new Promise(resolve => {
console.log(2);// 顺序-2
resolve(100);
})]).then(values => {
console.log(values);// 顺序- 10
});
process.nextTick(() => {
console.log(6);// 顺序- 6
});
new Promise((resolve) => {
console.log(3);// 顺序-3
resolve('3-1');
}).then(values => {
console.log(values);// 顺序- 8
});
console.log(4);// 顺序- 4
new Promise((resolve) => {
console.log(5);// 顺序- 5
resolve('5-1');
}).then(values => {
console.log(values);// 顺序- 9
})
process.nextTick(() => {
console.log(7);// 顺序- 7
})
これにはsetTimeoutが含まれ、タイマーには新しいPromise、Promise.then、およびPromise.allがあります。
このケースの実行順序は、最初のケースが実行された後に実行されます。実行シーケンスは次のとおりです。
複数のタイマーが同じ時間を持っている:setTimeoutが順番に実行されます。タイマーでの実行順序は、jsコードを順番に実行することです->新しいPromise->順序付けられたjsコード(jsと新しいPromiseが実行された後)->すべてのprocess.nextTick-> Promise()。then() -> Promise.all()。then()。これは、実際には最初のケースで実行の順序を繰り返しています。
setTimeout(() => {
console.log("setTimeout1");// 顺序-- 1
new Promise((resolve) => {
console.log('setTimeout1 --- Promise');// 顺序-- 2
resolve('Promise-1');
}).then((values) => {
console.log(values);// 顺序-- 5
});
}, 0)
setTimeout(() => {
console.log("setTimeout2");// 顺序-- 3
new Promise((resolve) => {
console.log('setTimeout2 --- Promise');// 顺序-- 4
resolve('Promise-2');
}).then((values) => {
console.log(values);// 顺序-- 6
});
}, 0)
マクロタスクとミクロタスク
メッセージキュー内のタスクは、次の2つのカテゴリに分類されます。
- マクロタスク(マクロタスク):setTimeout、setInterval
- マイクロタスク:約束
マクロタスクにはマイクロタスクが含まれており、1つのマクロタスクの実行が完了し、次のマクロタスクが開始されます。
console.log("a")
let r = new Promise(function (resolve, reject) {
console.log("b");
resolve()
});
r.then(() => console.log("c"));
setTimeout(() => {
console.log("d") }, 0)
setTimeout(() => {
console.log("e") }, 1000)
console.log("f")
これらの数行のコードを見てください
- 実行を開始し、いくつかのコードを見つけ、それをマクロタスクに入れました
- 最初に実行する
- bを実行します
- 非同期操作はマイクロタスクに属し、現在のマクロタスクのマイクロタスクのキューに追加されることがわかります。
- タイマーdとsetTimeoutがマクロタスクに属し、マクロタスクキューに追加されていることがわかります。
- タイマーeとsetTimeoutがマクロタスクに属し、マクロタスクのキューに追加されていることがわかります。
- fを実行します
- コードが実行され、マイクロタスクキューの実行が開始され、cが出力されます。
- マイクロタスクキューの実行が完了し、次のマクロタスクキューが実行されます
- dを直接印刷します。他のマイクロタスクはなく、次のマクロタスクキューの実行を開始します。
- eを印刷すると、すべてのマクロタスクが完了します