Node.js での Async 関数と Await 関数の使用

亚连
リリース: 2018-06-05 14:49:35
オリジナル
2606 人が閲覧しました

この記事では、Node.js の Async 関数と Await 関数に関する関連知識を主に紹介します。非常に優れており、必要な友人は参考にすることができます。この記事では、Node.js の async の使用方法を学びます。コールバックや Promise を簡素化する関数 (async/await)

非同期言語構造は、C# の async/await、Kotlin のコルーチン、Go のゴルーチンなど、すでに存在しています。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 <= MAX_RETRIES; i++) {
 try {
  return await request(url)
 } catch (err) {
  const timeout = Math.pow(2, i)
  console.log(&#39;Waiting&#39;, timeout, &#39;ms&#39;)
  await wait(timeout)
  console.log(&#39;Retrying&#39;, err.message, i)
 }
 }
}
ログイン後にコピー

上記のコードは非常に快適ですよね

中間値 3 つの非同期関数がある場合、相互依存関係がある場合は、前の例ほど怖くありません、その後、いくつかの醜い解決策から選択する必要があります。

functionA は Promise を返し、functionB はこの値を必要とし、functionA と functionB が完了した後の functionC は値を必要とします。

オプション 1: 次にクリスマスツリー

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 を取得できる値です。最初の関数 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)
}
ログイン後にコピー

配列反復メソッド あまり直感的ではないように見えるかもしれませんが、コンソールで次のコードを試すことができます。

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
ログイン後にコピー
マップ反復データの場合、戻り値は [2, 4, 6] であることがわかります。 , 8 ]、唯一の問題は、各値が AsyncFunction 関数によって Promise にラップされていることです

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

rreee

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

反復子内に長時間実行される同期ロジックと別の長時間実行される非同期タスクがある場合、async/await バージョンは依然として役立ちます

这种方式当你能拿到第一个值,就可以开始做一些计算,而不必等到所有 Promise 完成才运行你的计算。尽管结果包裹在 Promise 中,但是如果按顺序执行结果会更快。

关于 filter 的问题

你可能发觉了,即使上面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+的版本不妨尝试一下,或许会有新的收获。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何获取Vue中的this.$router.push参数

在angularJs-$http中如何实现百度搜索时的动态下拉框

在angularjs数组中如何判断是否含有某个元素

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

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート