非同期処理とは、非同期の手順に従って問題を処理することです。非同期処理と同期処理は対極にあり、それを生み出すものがマルチスレッドまたはマルチプロセスです。非同期処理のメリットは、デバイスの使用率が向上し、マクロレベルでのプログラムの動作効率が向上することですが、デメリットは、動作の競合やダーティデータの読み取りが発生しやすいことです。今回はJavaScriptの非同期処理について紹介します。
JavaScript の世界では、すべてのコードは単一のスレッドで実行されます。この「欠陥」のため、JavaScript のすべてのネットワーク操作とブラウザ イベントは非同期で実行する必要があります。非同期実行はコールバック関数を使用して実装できます
非同期操作は、将来の特定の時点で関数呼び出しをトリガーします
主流の非同期処理ソリューションには主に、コールバック関数 (CallBack)、Promise、Generator 関数、async/await が含まれます。
1. コールバック関数 (CallBack)
これは非同期プログラミングの最も基本的なメソッドです
最初のパラメーターはリクエストされた URL アドレスであり、2 番目のパラメーターはデータを取得するために使用されるとします。はコールバックです。 関数は次のとおりです。
function getData(url, callBack){ // 模拟发送网络请求 setTimeout(()=> { // 假设 res 就是返回的数据 var res = { url: url, data: Math.random() } // 执行回调,将数据作为参数传递 callBack(res) }, 1000) }
以下のように、サーバーに 3 回リクエストするシナリオを設定するとします。上記のコードの最初のリクエストの URL アドレスは /page/1?param=123 で、返される結果は res1 です。
2 番目のリクエストの URL アドレスは /page/2?param=${res1.data} で、これは最初のリクエストの res1.data に依存し、返される結果は res2` です。
3 番目のリクエストの URL アドレスは /page/3?param=${res2.data} で、これは 2 番目のリクエストの res2.data に依存し、返される結果は res3 です。
後続のリクエストは前のリクエストの結果に依存するため、次のリクエストは前のリクエストのコールバック関数内に記述することしかできず、よく言われる「コールバック地獄」が形成されます。
2. パブリッシュ/サブスクライブ
タスクが完了すると、シグナル センターにシグナルが「パブリッシュ」され、他のタスクがシグナル センターに「サブスクライブ」できるとします。シグナルは、いつ実行を開始できるかを知らせます。これは「パブリッシュ-サブスクライブ パターン」(パブリッシュ-サブスクライブ パターン) と呼ばれ、「オブザーバー パターン」(オブザーバー パターン) とも呼ばれます。次に示すのは、Ben Alman の Tiny Pub/Sub です。 jQuery 用のプラグイン
まず、f2 は「シグナル センター」にサブスクライブします。jQuery は「signal
getData('/page/1?param=123', (res1) => { console.log(res1) getData(`/page/2?param=${res1.data}`, (res2) => { console.log(res2) getData(`/page/3?param=${res2.data}`, (res3) => { console.log(res3) }) }) })
jQuery.subscribe("done", f2);
」このメソッドの性質は「イベント リスニング」に似ていますが、後者よりも大幅に優れています。なぜなら、「メッセージ センター」を見て、存在するシグナルの数と各シグナルの加入者数を確認することで、プログラムの動作を監視できるからです。
3. Promise
Promise は、従来のソリューションであるコールバック関数やイベントよりも合理的で強力です。いわゆる Promise は、特定のイベントの結果を格納するコンテナーです。 (通常は非同期操作) 将来終了します。構文的に言えば、Promise は非同期操作のメッセージを取得できるオブジェクトです。 Promise は統一された API を提供し、さまざまな非同期操作を同じ方法で処理できます。簡単に言えば、各非同期タスクが Promise オブジェクトを返し、そのオブジェクトにはコールバック関数を指定できる then メソッドが含まれます。
ここで、Promise を使用して上記のケースを再実装します。まず、データを非同期にリクエストするメソッドを Promise にカプセル化する必要があります
f1进行如下改写 function f1(){ setTimeout(function(){ // f1的任务代码 jQuery.publish("done"); }, 1000);} jQuery.publish("done") 的意思是, f1 执行完成后,向”信号中心 "jQuery 发布 "done" 信号,从而引发f2的执行。 此外,f2完成执行后,也可以取消订阅( unsubscribe ) jQuery.unsubscribe("done", f2);
その後、リクエスト コードは次のように記述する必要があります
function getDataAsync(url){ return new Promise((resolve, reject) => { setTimeout(()=> { var res = { url: url, data: Math.random() } resolve(res) }, 1000) }) }
その後、メソッドは新しい Promise オブジェクトを返します。 then メソッドの連鎖呼び出しは、CallBack コールバック地獄を回避します
。ただし、これは完璧ではありません。たとえば、多くの then ステートメントを追加する必要がある場合でも、各 then に対してコールバックを記述する必要があります。
シナリオがより複雑な場合、たとえば、後続の各リクエストが、前のリクエストの結果だけでなく、以前のすべてのリクエストの結果に依存する場合、シナリオはさらに複雑になります。 より良くするために、async/await を使用して実装する方法を見てみましょう
4. async/await
getDataAsync メソッドは次の通りですgetDataAsync('/page/1?param=123') .then(res1=> { console.log(res1) return getDataAsync(`/page/2?param=${res1.data}`) }) .then(res2=> { console.log(res2) return getDataAsync(`/page/3?param=${res2.data}`) }) .then(res3=> { console.log(res3) })
非同期関数 getData(){ var res1 = await getDataAsync('/page/1?param=123') console.log(res1) var res2 = await getDataAsync(`/page/2?param=${res1.data} `) console .log(res2) var res3 = await getDataAsync(`/page/2?param=${res2.data}`) console.log(res3)
}
asyncawait の使用は次のようであることがわかります。同期コードを書いていますPromise と比較してどう思いますか?非常に明確ですが、async/await は Promise に基づいています。実際、async で修飾されたメソッドは最終的に Promise を返すため、async/await は非同期構文シュガーを処理するために Generator 関数を使用していると見なすことができます。ジェネレーター関数は非同期を処理します 5. ジェネレーター
まず、非同期関数は次のように記述できます
function getDataAsync(url){ return new Promise((resolve, reject) => { setTimeout(()=> { var res = { url: url, data: Math.random() } resolve(res) }, 1000) }) }
var g = getData() g.next().value.then(res1=> { g.next(res1).value.then(res2=> { g.next(res2).value.then(()=> { g.next() }) }) })
上面的代码,我们逐步调用遍历器的 next() 方法,由于每一个 next() 方法返回值的 value 属性为一个 Promise 对象
所以我们为其添加 then 方法, 在 then 方法里面接着运行 next 方法挪移遍历器指针,直到 Generator 函数运行完成,实际上,这个过程我们不必手动完成,可以封装成一个简单的执行器
function run(gen){ var g = gen() function next(data){ var res = g.next(data) if (res.done) return res.value res.value.then((data) => { next(data) }) } next() }
run 方法用来自动运行异步的 Generator 函数,其实就是一个递归的过程调用的过程。这样我们就不必手动执行 Generator 函数了。 有了 run 方法,我们只需要这样运行 getData 方法
run(getData)
这样,我们就可以把异步操作封装到 Generator 函数内部,使用 run 方法作为 Generator 函数的自执行器,来处理异步。其实我们不难发现, async/await 方法相比于 Generator 处理异步的方式,有很多相似的地方,只不过 async/await 在语义化方面更加明显,同时 async/await 不需要我们手写执行器,其内部已经帮我们封装好了,这就是为什么说 async/await 是 Generator 函数处理异步的语法糖了
以上内容就是关于JavaScript中的异步处理,希望能帮助到大家。
相关推荐:
以上がJavaScriptによる非同期処理解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。