ホームページ > 記事 > ウェブフロントエンド > Node.js での Async 関数と Await 関数の使用
この記事では、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('Waiting', timeout, 'ms') await wait(timeout) console.log('Retrying', 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 で使用できなくなります。
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 を一緒に返します。これにより、クリスマス ツリーが実質的にフラット化されます (コールバック地獄)。
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+的版本不妨尝试一下,或许会有新的收获。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在angularJs-$http中如何实现百度搜索时的动态下拉框
以上がNode.js での Async 関数と Await 関数の使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。