JS非同期:実行原理とコールバック
1.JS非同期実行の原理
JavaScriptはシングルスレッドであり、ブラウザはマルチスレッドであることがわかっています。シングルスレッドの実行タスクは1つずつキューに入れる必要があります。タスクの実行に時間がかかる場合(ajaxに時間がかかる場合など)、直接応答がなく、次のタスクが実行を待機しています。このとき、非同期を使用する必要があります。
非同期性を理解するには、まず、ブラウザーに3つの基本的な常駐スレッド(JSエンジンスレッド、イベントトリガースレッド、およびGUIレンダリングスレッド)があることを知っておく必要があります。
JSエンジンスレッドとイベントトリガースレッドは一緒にイベントループメカニズムを構成し、GUIレンダリングスレッドとJSエンジンは相互に排他的です。JSエンジンが実行されると、GUIスレッドは一時停止され、GUI更新はに保存されます。キュー。JSエンジンがアイドル状態の場合、すぐに実行されます。
イベントループメカニズムから分析します
。JSエンジンスレッドは、同期タスクと非同期タスクに分けられます。
1.すべての同期タスクは、メインスレッドを介して実行され、実行スタックを形成します。
2.非同期タスクがある場合は、それを非同期プロセス(WebAPI)に渡します。イベントトリガースレッドまたはタイマースレッド処理を含めて、タスクキューを形成します。
3.実行スタック内のすべてのタスクが処理され、メインスレッドがアイドル状態になると、タスクはタスクキューから実行スタックに抽出されて実行されます。
素人の言葉で言えば、JavaScriptにはメインスレッドに加えてタスクキューもあります。タスクキューには非同期で実行する必要のあるコンテンツが格納されます。メインスレッドが実行された後も、タスクキュー内のタスクをスキャンして実行し続けます。キューが空になるまで。
解決策を描く:
写真のように、Xiaomingは学習に時間がかかるとDNFゲームをプレイできなくなるため、学習を非同期タスクキューに入れ、ゲーム(メインスレッド)をプレイした後に学習(タスクキュー)します。 。期間中、母親は学習イベント(DOMイベント)を追加し、Xiaomingは、Xiaomingが学習タスクを完了するたびに、最終的な完了まで、他のタスクが何であるかを確認しました(周期的スキャン)。
別の例を見てみましょう(ブラウザが更新され、ボタンをクリックし続けます):
let myData = null
//ajax请求
function ajax() {
//腾讯新冠实时数据接口,仅做学习
axios.get('https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=chinaDayList,chinaDayAddList,nowConfirmStatis,provinceCompare')
.then(data => {
console.log("ajax返回成功");
myData = data.data
console.log(myData);
})
.catch(error => {
console.log("ajax返回失败");
})
}
console.log(myData);
ajax()
setTimeout(() => {
console.log('定时器');
}, 2000);
console.log(myData);
const btn = document.querySelector('button')
btn.onclick = () => {
console.log("点击了");
}
ヌル
ヌル
AJAXを返す成功
オブジェクトが
クリックされた
タイマー
クリックを
コンソールはメインスレッドで同期的に実行され、最初に実行され、メインスレッド外のタスクキューには非同期実行の内容が格納されていることがわかります。setTimeout、ajax、およびDOMイベントは、次の順序で実行されます。タスクキュー(循環スキャンキュー)。
なぜループでスキャンしたいのですか?
クリックイベントから、ユーザーが操作すると(クリックイベント、スクロールイベント、ウィンドウサイズ変更イベントなど)、新しいイベントがイベントループのタスクキューに追加され、実行を待機することがわかります。サイクリックスキャンが必要です。
2つ目は、JS非同期のコールバックです。
非同期は最後のタスクキューで実行されるため、ロジックの多くを実装するのは困難です。現時点では、この非同期ロジックを処理する必要があります。最も一般的に使用される方法は、コールバックコールバックです。
コールバック関数:簡単に言えば、関数Aがパラメーターとして関数Bに渡されると、関数Bは関数Aによって実行されるコールバック関数です。コールバックには、ネストされたコールバックとチェーンされたコールバックの2種類があります。
コールバックの簡単な使用法は次のとおりです。
let myData = null
console.log(myData);
setTimeout(() => {
console.log('定时器');
}, 2000);
const btn = document.querySelector('button')
btn.onclick = () => {
console.log("点击了");
}
let name = "张三"
function hr(callback) {
setTimeout(() => {
console.log(`我是${
name}`);
callback();
}, 2001);
}
console.log(myData);
function gj() {
console.log(`${
name}你好,我是李四,认识一下吧`);
}
hr(gj)
ヌル
ヌル
クリックされた
タイマー
私はチャンだ
サンチャン私は李斯よ、サンこんにちは、あなたは知ってみましょう
クリック
明らかに、関数がデータを必要とするときにコールバックを使用し、ここでは非同期コールバックが使用されます。
コールバックは非同期を解決するための一般的な方法ですが、JSのニーズはますます複雑になっています。同期および非同期には、ますます多くのコールバック実装ロジックが必要です。同期と非同期、および過度のコールバックのネストとインデントが混在すると、コードの解釈と保守が困難になり、「コールバック地獄」が形成されます。
例を見てみましょう:
const verifyUser = function(username, password, callback){
dataBase.verifyUser(username, password, (error, userInfo) => {
if (error) {
callback(error)
}else{
dataBase.getRoles(username, (error, roles) => {
if (error){
callback(error)
}else {
dataBase.logAccess(username, (error) => {
if (error){
callback(error);
}else{
callback(null, userInfo, roles);
}
})
}
})
}
})
};
ほとんどの人は上記のコードを見て、脳が凍りつくような味を感じます。プロジェクトにそのようなコードブロックが何百もあると、しばらくすると、それを書いた人でさえ頭痛がするだろうと思います。あなた自身のプロジェクトに来ることは地獄に来るようなものです。
最も重要なことは、同時に、コールバックにはまだ信頼の問題があり、サードパーティ(ajaxなど)に実行制御を与えることです。信頼の問題を解決するには、コールバックによって引き起こされる信頼の問題を解決するために、プログラムにさまざまなロジックを記述する必要があります。
・呼び出しが早すぎます
・呼び出しが終了しました
・呼び出しの数が多すぎるか少なすぎて、必要なパラメーターがコールバック関数に正常に渡されませんでした。
・発生する可能性のあるエラーが飲み込まれました。
特定の信頼の問題を解決するために特定のロジックを作成することは、それ自体のアプリケーションの価値よりも困難であり、コードの冗長性や読みやすさの低下などの問題も引き起こすことがわかります。
要約すると、コールバックは非同期の欠陥を解決します:
1)それは人々のタスク処理の論理的思考に適合しません
2)コールバックによって引き起こされる信頼の問題。
コールバックのますます明白な欠点に直面して、ES6は非同期の問題を解決するためにPromiseを更新しました。次の記事はES6-Promiseを書いています。