JavaScript 非同期進化の歴史のコード例の詳細な紹介
はじめに
JS の最も基本的な非同期呼び出しメソッドはコールバックです。非同期の完了後、ブラウザまたはノードはコールバック関数のコールバックを非同期 API に渡します。単純な非同期操作の場合は、コールバックを使用するだけで十分です。しかし、インタラクティブなページとノードの出現により、コールバック ソリューションの欠点が明らかになり始めました。 Promise 仕様が誕生し、ES6 仕様に組み込まれました。その後、ES7 では Promise に基づいて非同期機能が標準に組み込まれました。これは JavaScript の非同期進化の歴史です。
同期と非同期
通常、コードは上から下に実行されます。複数のタスクがある場合は、次のタスクが実行される前に前のタスクが完了する必要があります。この実行モードは同期と呼ばれます。初心者は、コンピュータ言語での同期と日常言語での同期を簡単に混同してしまいます。たとえば、「ファイルをクラウドに同期する」の同期は、「一貫性を保つ」ことを指します。コンピュータにおいて、同期とは、タスクが上から下に順番に実行されるモードを指します。例:
A(); B(); C();
このコードでは、A、B、C は 3 つの異なる関数であり、各関数は無関係のタスクです。同期モードでは、コンピューターはタスク A を実行し、次にタスク B、最後にタスク C を実行します。ほとんどの場合、同期モードで問題ありません。ただし、タスク B が長時間実行されるネットワーク リクエストで、タスク C がたまたま新しいページを表示している場合、Web ページがフリーズします。
より良い解決策は、タスク B を 2 つの部分に分割することです。 1 つの部分はネットワークから要求されたタスクを即座に実行し、もう 1 つの部分は要求が返された後にタスクを実行します。一方の部分がすぐに実行され、もう一方の部分が後で実行されるこのパターンは、非同期と呼ばれます。
A(); // 在现在发送请求 ajax('url1',function B() { // 在未来某个时刻执行 }) C(); // 执行顺序 A => C => B
実際、JS エンジンはネットワーク リクエストを直接処理するのではなく、ブラウザのネットワーク リクエスト インターフェイスを呼び出すだけで、ブラウザはネットワーク リクエストを送信し、返されたデータを監視します。 JavaScript の非同期機能の本質は、ブラウザまたはノードのマルチスレッド機能です。
callback
将来実行される関数は通常コールバックと呼ばれます。コールバックの非同期モードを使用すると、ブロッキングの問題は解決されますが、他の問題も発生します。当初、関数は上から下に記述され、上から下に実行されていましたが、この「線形」モードは私たちの思考習慣と非常に一致していますが、現在はコールバックによって中断されています。上記のコードでは、タスク B をスキップして、タスク C を最初に実行します。この種の非同期「非線形」コードは、同期「線形」コードよりも読みにくく、したがってバグが発生する可能性が高くなります。
次のコードの実行順序を判断してみてください。「非線形」コードは「線形」コードよりも読みにくいということがより深く理解できるようになります。
A(); ajax('url1', function(){ B(); ajax('url2', function(){ C(); } D(); }); E(); // A => E => B => D => C
このコードでは、上から下への実行順序がコールバックによって乱れています。コードを読むときの視線は A => B => D => E
ですが、実行順序は A => E => です。 B => ; D => C
、これは非線形コードの悪い点です。 A => B => C => D => E
,但是执行顺序却是A => E => B => D => C
,这就是非线性代码带来的糟糕之处。
通过将ajax
后面执行的任务提前,可以更容易看懂代码的执行顺序。虽然代码因为嵌套看起来不美观,但现在的执行顺序却是从上到下的“线性”方式。这种技巧在写多重嵌套的代码时,是非常有用的。
A(); E(); ajax('url1', function(){ B(); D(); ajax('url2', function(){ C(); } }); // A => E => B => D => C
上一段代码只有处理了成功回调,并没处理异常回调。接下来,把异常处理回调加上,再来讨论代码“线性”执行的问题。
A(); ajax('url1', function(){ B(); ajax('url2', function(){ C(); },function(){ D(); }); },function(){ E(); });
加上异常处理回调后,url1
的成功回调函数 B 和异常回调函数 E,被分开了。这种“非线性”的情况又出现了。
在 node 中,为了解决的异常回调导致的“非线性”的问题,制定了错误优先的策略。node 中 callback 的第一个参数,专门用于判断是否发生异常。
A(); get('url1', function(error){ if(error){ E(); }else { B(); get('url2', function(error){ if(error){ D(); }else{ C(); } }); } });
到此,callback 引起的“非线性”问题基本得到解决。遗憾的是,使用 callback 嵌套,一层层if else
和回调函数,一旦嵌套层数多起来,阅读起来不是很方便。此外,callback 一旦出现异常,只能在当前回调函数内部处理异常。
promise
在 JavaScript 的异步进化史中,涌现出一系列解决 callback 弊端的库,而 Promise 成为了最终的胜者,并成功地被引入了 ES6 中。它将提供了一个更好的“线性”书写方式,并解决了异步异常只能在当前回调中被捕获的问题。
Promise 就像一个中介,它承诺会将一个可信任的异步结果返回。首先 Promise 和异步接口签订一个协议,成功时,调用resolve
函数通知 Promise,异常时,调用reject
通知 Promise。另一方面 Promise 和 callback 也签订一个协议,由 Promise 在将来返回可信任的值给then
和catch
ajax
の後に実行されるタスクを進めることで、コードの実行順序が理解しやすくなります。コードは入れ子になっているため見苦しくなりますが、実行順序は上から下へ「線形」になります。この手法は、複数のネストされたコードを作成する場合に非常に役立ちます。 🎜// 创建一个 Promise 实例(异步接口和 Promise 签订协议) var promise = new Promise(function (resolve,reject) { ajax('url',resolve,reject); }); // 调用实例的 then catch 方法 (成功回调、异常回调与 Promise 签订协议) promise.then(function(value) { // success }).catch(function (error) { // error })
var promise1 = new Promise(function (resolve) { // 可能由于某些原因导致同步调用 resolve('B'); }); // promise依旧会异步执行 promise1.then(function(value){ console.log(value) }); console.log('A'); // A B (先 A 后 B) var promise2 = new Promise(function (resolve) { // 成功回调被通知了2次 setTimeout(function(){ resolve(); },0) }); // promise只会调用一次 promise2.then(function(){ console.log('A') }); // A (只有一个) var promise3 = new Promise(function (resolve,reject) { // 成功回调先被通知,又通知了失败回调 setTimeout(function(){ resolve(); reject(); },0) }); // promise只会调用成功回调 promise3.then(function(){ console.log('A') }).catch(function(){ console.log('B') }); // A(只有A)
url1
の成功コールバック関数Bと例外コールバック関数Eが分離されます。この「非線形」状況が再び現れます。 🎜🎜ノードでは、異常なコールバックによって引き起こされる「非線形」問題を解決するために、エラー優先戦略が策定されました。ノードのコールバックの最初のパラメータは、例外が発生したかどうかを判断するために特に使用されます。 🎜var fetch = function(url){ // 返回一个新的 Promise 实例 return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } A(); fetch('url1').then(function(){ B(); // 返回一个新的 Promise 实例 return fetch('url2'); }).catch(function(){ // 异常的时候也可以返回一个新的 Promise 实例 return fetch('url2'); // 使用链式写法调用这个新的 Promise 实例的 then 方法 }).then(function() { C(); // 继续返回一个新的 Promise 实例... }) // A B C ...
if else
のレイヤー、およびコールバック関数を使用すると、ネストされたレイヤーの数が増えると、読み取りがあまり便利ではなくなります。さらに、コールバックで例外が発生すると、その例外は現在のコールバック関数内でのみ処理できます。 🎜🎜promise🎜🎜 JavaScript の非同期進化の歴史の中で、コールバックの欠点を解決するために一連のライブラリが登場し、Promise が最終的な勝者となり、ES6 への導入に成功しました。これにより、より優れた「線形」の記述方法が提供され、非同期例外が現在のコールバックでのみキャッチできるという問題が解決されます。 🎜🎜Promise は、信頼できる非同期結果を返すことを約束する仲介者のようなものです。まず、Promise は非同期インターフェースとの合意に署名します。成功すると、resolve
関数が呼び出され、例外が発生すると、reject
が呼び出され、Promise に通知されます。一方で、Promise とコールバックも契約を締結しており、将来的に Promise は then
と catch
に登録されたコールバックに信頼できる値を返すことになります。 🎜// 创建一个 Promise 实例(异步接口和 Promise 签订协议) var promise = new Promise(function (resolve,reject) { ajax('url',resolve,reject); }); // 调用实例的 then catch 方法 (成功回调、异常回调与 Promise 签订协议) promise.then(function(value) { // success }).catch(function (error) { // error })
Promise 是个非常不错的中介,它只返回可信的信息给 callback。它对第三方异步库的结果进行了一些加工,保证了 callback 一定会被异步调用,且只会被调用一次。
var promise1 = new Promise(function (resolve) { // 可能由于某些原因导致同步调用 resolve('B'); }); // promise依旧会异步执行 promise1.then(function(value){ console.log(value) }); console.log('A'); // A B (先 A 后 B) var promise2 = new Promise(function (resolve) { // 成功回调被通知了2次 setTimeout(function(){ resolve(); },0) }); // promise只会调用一次 promise2.then(function(){ console.log('A') }); // A (只有一个) var promise3 = new Promise(function (resolve,reject) { // 成功回调先被通知,又通知了失败回调 setTimeout(function(){ resolve(); reject(); },0) }); // promise只会调用成功回调 promise3.then(function(){ console.log('A') }).catch(function(){ console.log('B') }); // A(只有A)
介绍完 Promise 的特性后,来看看它如何利用链式调用,解决异步代码可读性的问题的。
var fetch = function(url){ // 返回一个新的 Promise 实例 return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } A(); fetch('url1').then(function(){ B(); // 返回一个新的 Promise 实例 return fetch('url2'); }).catch(function(){ // 异常的时候也可以返回一个新的 Promise 实例 return fetch('url2'); // 使用链式写法调用这个新的 Promise 实例的 then 方法 }).then(function() { C(); // 继续返回一个新的 Promise 实例... }) // A B C ...
如此反复,不断返回一个 Promise 对象,再采用链式调用的方式不断地调用。使 Promise 摆脱了 callback 层层嵌套的问题和异步代码“非线性”执行的问题。
Promise 解决的另外一个难点是 callback 只能捕获当前错误异常。Promise 和 callback 不同,每个 callback 只能知道自己的报错情况,但 Promise 代理着所有的 callback,所有 callback 的报错,都可以由 Promise 统一处理。所以,可以通过catch
来捕获之前未捕获的异常。
Promise 解决了 callback 的异步调用问题,但 Promise 并没有摆脱 callback,它只是将 callback 放到一个可以信任的中间机构,这个中间机构去链接我们的代码和异步接口。
异步(async)函数
异步(async)函数是 ES7 的一个新的特性,它结合了 Promise,让我们摆脱 callback 的束缚,直接用类同步的“线性”方式,写异步函数。
声明异步函数,只需在普通函数前添加一个关键字 async
即可,如async function main(){}
。在异步函数中,可以使用await
关键字,表示等待后面表达式的执行结果,一般后面的表达式是 Promise 实例。
async function main{ // timer 是在上一个例子中定义的 var value = await timer(100); console.log(value); // done (100ms 后返回 done) } main();
异步函数和普通函数一样调用 main()
。调用后,会立即执行异步函数中的第一行代码 var value = await timer(100)
。等到异步执行完成后,才会执行下一行代码。
除此之外,异步函数和其他函数基本类似,它使用try...catch
来捕捉异常。也可以传入参数。但不要在异步函数中使用return
来返回值。
var timer = new Promise(function create(resolve,reject) { if(typeof delay !== 'number'){ reject(new Error('type error')); } setTimeout(resolve,delay,'done'); }); async function main(delay){ try{ var value1 = await timer(delay); var value2 = await timer(''); var value3 = await timer(delay); }catch(err){ console.error(err); // Error: type error // at create (<anonymous>:5:14) // at timer (<anonymous>:3:10) // at A (<anonymous>:12:10) } } main(0);
异步函数也可以被当作值,传入普通函数和异步函数中执行。但是在异步函数中,使用异步函数时要注意,如果不使用await
,异步函数会被同步执行。
async function main(delay){ var value1 = await timer(delay); console.log('A') } async function doAsync(main){ main(0); console.log('B') } doAsync(main); // B A
这个时候打印出来的值是 B A
。说明 doAsync
函数并没有等待 main
的异步执行完毕就执行了 console
。如果要让 console
在main
的异步执行完毕后才执行,我们需要在main
前添加关键字await
。
async function main(delay){ var value1 = await timer(delay); console.log('A') } async function doAsync(main){ await main(0); console.log('B') } doAsync(main); // A B
由于异步函数采用类同步的书写方法,所以在处理多个并发请求,新手可能会像下面一样书写。这样会导致url2
的请求必需等到url1
的请求回来后才会发送。
var fetch = function (url) { return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } async function main(){ try{ var value1 = await fetch('url1'); var value2 = await fetch('url2'); conosle.log(value1,value2); }catch(err){ console.error(err) } } main();
使用Promise.all
的方法来解决这个问题。Promise.all
用于将多个Promise实例,包装成一个新的 Promis e实例,当所有的 Promise 成功后才会触发Promise.all
的resolve
函数,当有一个失败,则立即调用Promise.all
的reject
函数。
var fetch = function (url) { return new Promise(function (resolve,reject) { ajax(url,resolve,reject); }); } async function main(){ try{ var arrValue = await Promise.all[fetch('url1'),fetch('url2')]; conosle.log(arrValue[0],arrValue[1]); }catch(err){ console.error(err) } } main();
目前使用 Babel 已经支持 ES7 异步函数的转码了,大家可以在自己的项目中开始尝试。
以上就是JavaScript 异步进化史的代码实例详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

使用法: JavaScript では、insertBefore() メソッドを使用して、DOM ツリーに新しいノードを挿入します。このメソッドには、挿入される新しいノードと参照ノード (つまり、新しいノードが挿入されるノード) の 2 つのパラメータが必要です。

JavaScript で HTTP ステータス コードを取得する方法の紹介: フロントエンド開発では、バックエンド インターフェイスとの対話を処理する必要があることが多く、HTTP ステータス コードはその非常に重要な部分です。 HTTP ステータス コードを理解して取得すると、インターフェイスから返されたデータをより適切に処理できるようになります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法と、具体的なコード例を紹介します。 1. HTTP ステータス コードとは何ですか? HTTP ステータス コードとは、ブラウザがサーバーへのリクエストを開始したときに、サービスが
