検索
ホームページウェブフロントエンドjsチュートリアルNode.js での Async および Await 関数の分析

Node.js での Async および Await 関数の分析

Feb 24, 2018 am 09:10 AM
awaitjavascriptnode.js

この記事では、Node.js の Async 関数と Await 関数の関連知識を主に紹介します。Node.js で async 関数 (async/await) を使用してコールバックや Promise を簡素化する方法を学びます。参考値、困っている友達が参考になれば幸いです。

非同期言語構造は、C# の async/await、Kotlin のコルーチン、go の goroutine など、他の言語にすでに存在しています。Node.js 8 のリリースでは、待望の async 関数もデフォルトで実装されました。

Nodeの非同期関数とは何ですか?

関数が Async 関数として宣言されると、AsyncFunction オブジェクトが返されます。実行を一時停止できるという点で Generator に似ています。唯一の違いは、{ value: any、done: Boolean } オブジェクトの代わりに Promise を返すことです。ただし、これらは依然として非常に似ており、co パッケージを使用して同じ機能を取得できます。

非同期関数では、Promise が完了するまで待つことも、拒否された理由を取得することもできます。

Promise に独自のロジックを実装したい場合

function handler (req, res) {
 return request('https://user-handler-service')
 .catch((err) => {
  logger.error('Http error', err)
  error.logged = true
  throw err
 })
 .then((response) => Mongo.findOne({ user: response.body.user }))
 .catch((err) => {
  !error.logged && logger.error('Mongo error', err)
  error.logged = true
  throw err
 })
 .then((document) => executeLogic(req, res, document))
 .catch((err) => {
  !error.logged && console.error(err)
  res.status(500).send()
 })
}

async/await を使用して、このコードを同期的に実行されるコードのように見せることができます

async function handler (req, res) {
 let response
 try {
 response = await request('https://user-handler-service') 
 } catch (err) {
 logger.error('Http error', err)
 return res.status(500).send()
 }
 let document
 try {
 document = await Mongo.findOne({ user: response.body.user })
 } catch (err) {
 logger.error('Mongo error', err)
 return res.status(500).send()
 }
 executeLogic(document, req, res)
}

古い v8 バージョンでは、Promise の拒否がある場合、 ? が処理されると警告が表示されますが、拒否エラー リスニング関数を作成する必要はありません。ただし、この場合はアプリケーションを終了することをお勧めします。エラーを処理しないと、アプリケーションは不明な状態になるためです。

process.on('unhandledRejection', (err) => { 
 console.error(err)
 process.exit(1)
})

async 関数パターン

非同期操作を扱う場合、非同期操作を同期コードのように見せる例がたくさんあります。 Promise またはコールバックを使用して問題を解決する場合は、非常に複雑なパターンまたは外部ライブラリを使用する必要があります。

ループ内でデータの非同期取得を使用したり、if-else 条件を使用したりする必要がある場合は、非常に複雑な状況になります。

指数関数的ロールバック メカニズム

Promise を使用してロールバック ロジックを実装するのは非常に不格好です

function requestWithRetry (url, retryCount) {
 if (retryCount) {
 return new Promise((resolve, reject) => {
  const timeout = Math.pow(2, retryCount)
  setTimeout(() => {
  console.log('Waiting', timeout, 'ms')
  _requestWithRetry(url, retryCount)
   .then(resolve)
   .catch(reject)
  }, timeout)
 })
 } else {
 return _requestWithRetry(url, 0)
 }
}
function _requestWithRetry (url, retryCount) {
 return request(url, retryCount)
 .catch((err) => {
  if (err.statusCode && err.statusCode >= 500) {
  console.log('Retrying', err.message, retryCount)
  return requestWithRetry(url, ++retryCount)
  }
  throw err
 })
}
requestWithRetry('http://localhost:3000')
 .then((res) => {
 console.log(res)
 })
 .catch(err => {
 console.error(err)
 })

コードは見るのが非常に面倒なので、そのようなコードは見たくないでしょう。この例を async/await を使用してやり直すと、より簡単になります

function wait (timeout) {
 return new Promise((resolve) => {
 setTimeout(() => {
  resolve()
 }, timeout)
 })
}

async function requestWithRetry (url) {
 const MAX_RETRIES = 10
 for (let i = 0; i <p>上記のコードはとても快適ですよね?</p><p>中間値</p><p>は、それぞれに依存する 3 つの async 関数がある場合、前の例ほど怖くありません他の場合は、いくつかの醜い解決策から選択する必要があります。 </p><p>functionA は Promise を返し、functionB はこの値を必要とし、functionA と functionB が完了した後の functionC は値を必要とします。 </p><p>オプション 1: 次にクリスマスツリー</p><pre class="brush:php;toolbar:false">function executeAsyncTask () {
 return functionA()
 .then((valueA) => {
  return functionB(valueA)
  .then((valueB) => {   
   return functionC(valueA, valueB)
  })
 })
}

この解決策を使用すると、3 番目の then で valueA と valueB を取得でき、その後、前の 2 つの then と同様に valueA と valueB の値を取得できます。ここではクリスマス ツリーを平らにすることはできません (破滅地獄)。平らにするとクロージャが失われ、valueA が functionC で使用できなくなります。

オプション 2: 上位レベルのスコープに移動

function executeAsyncTask () {
 let valueA
 return functionA()
 .then((v) => {
  valueA = v
  return functionB(valueA)
 })
 .then((valueB) => {
  return functionC(valueA, valueB)
 })
}

このクリスマス ツリーでは、上位のスコープ リテイナー valueA を使用します。これは、valueA のスコープがすべてのスコープの外側にあるため、functionC は functionA によって完成された最初の A 値を取得できます。

これは .then チェーンをフラット化するための非常に「正しい」構文ですが、この方法では同じ値を保持するために 2 つの変数 valueA と v を使用する必要があります。

オプション 3: 追加の配列を使用する

function executeAsyncTask () {
 return functionA()
 .then(valueA => {
  return Promise.all([valueA, functionB(valueA)])
 })
 .then(([valueA, valueB]) => {
  return functionC(valueA, valueB)
 })
}

functionA の then で配列を使用して、valueA と Promise を一緒に返します。これにより、クリスマス ツリーを効果的に平坦化できます (コールバック地獄)。

オプション 4: ヘルパー関数を作成する

const converge = (...promises) => (...args) => {
 let [head, ...tail] = promises
 if (tail.length) {
 return head(...args)
  .then((value) => converge(...tail)(...args.concat([value])))
 } else {
 return head(...args)
 }
}
functionA(2)
 .then((valueA) => converge(functionB, functionC)(valueA))

これは実行可能です。コンテキスト変数宣言をマスクするヘルパー関数を作成します。しかし、そのようなコードは、特にこれらの魔法に慣れていない人にとっては非常に読みにくいです。

async/await を使用すると、問題は魔法のように消えます

async function executeAsyncTask () {
 const valueA = await functionA()
 const valueB = await functionB(valueA)
 return function3(valueA, valueB)
}

async/await を使用して複数の並列リクエストを処理します

上記と同様に、複数の非同期タスクを一度に実行し、それらを別の場所で使用したい場合async/await を使用すると簡単に判断できます。

async function executeParallelAsyncTasks () {
 const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ])
 doSomethingWith(valueA)
 doSomethingElseWith(valueB)
 doAnotherThingWith(valueC)
}

配列反復メソッド

map、filter、reduce メソッドで非同期関数を使用できます。あまり直感的ではないように見えますが、コンソールで次のコードを試すことができます。

1.map

function asyncThing (value) {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve(value), 100)
 })
}

async function main () {
 return [1,2,3,4].map(async (value) => {
 const v = await asyncThing(value)
 return v * 2
 })
}

main()
 .then(v => console.log(v))
 .catch(err => console.error(err))

2.filter

function asyncThing (value) {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve(value), 100)
 })
}
async function main () {
 return [1,2,3,4].filter(async (value) => {
 const v = await asyncThing(value)
 return v % 2 === 0
 })
}
main()
 .then(v => console.log(v))
 .catch(err => console.error(err))

3.reduce

function asyncThing (value) {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve(value), 100)
 })
}
async function main () {
 return [1,2,3,4].reduce(async (acc, value) => {
 return await acc + await asyncThing(value)
 }, Promise.resolve(0))
}
main()
 .then(v => console.log(v))
 .catch(err => console.error(err))

解決策:

[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]
[ 1, 2, 3, 4 ]
10</pending></pending></pending></pending>

マップ反復データの場合、戻り値は [2, 4, 6, 8] であることがわかります。唯一の問題は、各値が AsyncFunction 関数によって Promise にラップされていることです

したがって、それらの値を取得したい場合は、配列を Promise.All() に渡して Promise のラップを解除する必要があります。

rreee

これは簡単だと思いますか?

イテレーター内に長時間実行される同期ロジックと別の長時間実行される非同期タスクがある場合、async/await バージョンは引き続き役立ちます

このようにして、最初の値を取得できたら、何もしなくても計算を開始できます。計算を実行する前に、すべての Promise が完了するまで待機します。結果は Promise でラップされますが、結果を順番に実行した方が高速です。

フィルターに関する質問

你可能发觉了,即使上面filter函数里面返回了 [ false, true, false, true ] , await asyncThing(value) 会返回一个 promise 那么你肯定会得到一个原始的值。你可以在return之前等待所有异步完成,在进行过滤。

Reducing很简单,有一点需要注意的就是需要将初始值包裹在 Promise.resolve 中

重写基于callback的node应用成

Async 函数默认返回一个 Promise ,所以你可以使用 Promises 来重写任何基于 callback 的函数,然后 await 等待他们执行完毕。在node中也可以使用 util.promisify 函数将基于回调的函数转换为基于 Promise 的函数

重写基于Promise的应用程序

要转换很简单, .then 将Promise执行流串了起来。现在你可以直接使用`async/await。

function asyncTask () {
 return functionA()
  .then((valueA) => functionB(valueA))
  .then((valueB) => functionC(valueB))
  .then((valueC) => functionD(valueC))
  .catch((err) => logger.error(err))
}

转换后

async function asyncTask () {
 try {
  const valueA = await functionA()
  const valueB = await functionB(valueA)
  const valueC = await functionC(valueB)
  return await functionD(valueC)
 } catch (err) {
  logger.error(err)
 }
}
Rewriting Nod

使用 Async/Await 将很大程度上的使应用程序具有高可读性,降低应用程序的处理复杂度(如:错误捕获),如果你也使用 node v8+的版本不妨尝试一下,或许会有新的收获。

相关推荐:

ES6之async+await 同步/异步方案

NodeJs通过async和await处理异步的方法

ES7的async/await用法实例详解

以上がNode.js での Async および Await 関数の分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
Java vs JavaScript:開発者向けの詳細な比較Java vs JavaScript:開発者向けの詳細な比較May 16, 2025 am 12:01 AM

Javaandjavascriptaredistinctlanguages:javaisusedforenterpriseandmobileapps、whilejavascriptisforinteractivewebpages.1)javaiscompiled、staticatically、andrunsonjvm.2)javascriptisisterted、dynamsornoded.3)

JavaScriptのデータ型:ブラウザとNodejsに違いはありますか?JavaScriptのデータ型:ブラウザとNodejsに違いはありますか?May 14, 2025 am 12:15 AM

JavaScriptコアデータ型は、ブラウザとnode.jsで一貫していますが、余分なタイプとは異なる方法で処理されます。 1)グローバルオブジェクトはブラウザのウィンドウであり、node.jsのグローバルです2)バイナリデータの処理に使用されるNode.jsの一意のバッファオブジェクト。 3)パフォーマンスと時間の処理にも違いがあり、環境に従ってコードを調整する必要があります。

JavaScriptコメント://および / * *を使用するためのガイドJavaScriptコメント://および / * *を使用するためのガイドMay 13, 2025 pm 03:49 PM

javascriptusestwotypesofcomments:シングルライン(//)およびマルチライン(//)

Python vs. JavaScript:開発者の比較分析Python vs. JavaScript:開発者の比較分析May 09, 2025 am 12:22 AM

PythonとJavaScriptの主な違いは、タイプシステムとアプリケーションシナリオです。 1。Pythonは、科学的コンピューティングとデータ分析に適した動的タイプを使用します。 2。JavaScriptは弱いタイプを採用し、フロントエンドとフルスタックの開発で広く使用されています。この2つは、非同期プログラミングとパフォーマンスの最適化に独自の利点があり、選択する際にプロジェクトの要件に従って決定する必要があります。

Python vs. JavaScript:ジョブに適したツールを選択するPython vs. JavaScript:ジョブに適したツールを選択するMay 08, 2025 am 12:10 AM

PythonまたはJavaScriptを選択するかどうかは、プロジェクトの種類によって異なります。1)データサイエンスおよび自動化タスクのPythonを選択します。 2)フロントエンドとフルスタック開発のためにJavaScriptを選択します。 Pythonは、データ処理と自動化における強力なライブラリに好まれていますが、JavaScriptはWebインタラクションとフルスタック開発の利点に不可欠です。

PythonとJavaScript:それぞれの強みを理解するPythonとJavaScript:それぞれの強みを理解するMay 06, 2025 am 12:15 AM

PythonとJavaScriptにはそれぞれ独自の利点があり、選択はプロジェクトのニーズと個人的な好みに依存します。 1. Pythonは、データサイエンスやバックエンド開発に適した簡潔な構文を備えた学習が簡単ですが、実行速度が遅くなっています。 2。JavaScriptはフロントエンド開発のいたるところにあり、強力な非同期プログラミング機能を備えています。 node.jsはフルスタックの開発に適していますが、構文は複雑でエラーが発生しやすい場合があります。

JavaScriptのコア:CまたはCの上に構築されていますか?JavaScriptのコア:CまたはCの上に構築されていますか?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc;それは、解釈されていることを解釈しました。

JavaScriptアプリケーション:フロントエンドからバックエンドまでJavaScriptアプリケーション:フロントエンドからバックエンドまでMay 04, 2025 am 12:12 AM

JavaScriptは、フロントエンドおよびバックエンド開発に使用できます。フロントエンドは、DOM操作を介してユーザーエクスペリエンスを強化し、バックエンドはnode.jsを介してサーバータスクを処理することを処理します。 1.フロントエンドの例:Webページテキストのコンテンツを変更します。 2。バックエンドの例:node.jsサーバーを作成します。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

SublimeText3 中国語版

SublimeText3 中国語版

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

SublimeText3 Mac版

SublimeText3 Mac版

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