Dieser Artikel stellt hauptsächlich das relevante Wissen über Async- und Await-Funktionen in Node.js vor. Es ist sehr gut und hat Referenzwert.
In diesem Artikel erfahren Sie, wie Verwenden Sie die asynchrone Funktion (async/await) in Node.js, um Callback oder Promise zu vereinfachen.
Asynchrone Sprachstrukturen gibt es bereits in anderen Sprachen, wie z. B. async/await von c#, den Coroutinen von Kotlin und den Goroutinen von go Mit der Veröffentlichung von Node.js 8 ist auch die lang erwartete Async-Funktion standardmäßig implementiert.
Was ist die asynchrone Funktion in Node?
Wenn eine Funktion als Async-Funktion deklariert wird, gibt sie ein AsyncFunction-Objekt zurück. Sie ähneln Generatoren darin, dass die Ausführung angehalten werden kann. Der einzige Unterschied besteht darin, dass sie ein Promise anstelle eines { value: any, done: Boolean }-Objekts zurückgeben. Sie sind sich jedoch immer noch sehr ähnlich und Sie können das Co-Paket verwenden, um die gleiche Funktionalität zu erhalten.
In einer asynchronen Funktion können Sie warten, bis das Versprechen abgeschlossen ist, oder den Grund für seine Ablehnung erfassen.
Wenn Sie einen Teil Ihrer eigenen Logik in Promise implementieren möchten
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() }) }
Sie können async/await verwenden, um diesen Code wie synchron ausgeführten Code aussehen zu lassen
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) }
Im alten Wenn in der v8-Version eine Versprechenablehnung vorliegt, die nicht behandelt wird, erhalten Sie eine Warnung und müssen keine Funktion zum Abhören von Ablehnungsfehlern erstellen. Es wird jedoch empfohlen, in diesem Fall Ihre Bewerbung zu beenden. Denn wenn Sie Fehler nicht behandeln, befindet sich die Anwendung in einem unbekannten Zustand.
process.on('unhandledRejection', (err) => { console.error(err) process.exit(1) })
asynchrones Funktionsmuster
Beim Umgang mit asynchronen Vorgängen gibt es viele Beispiele dafür, dass sie wie synchroner Code aussehen. Wenn Sie Promise oder Rückrufe verwenden, um das Problem zu lösen, müssen Sie ein sehr komplexes Muster oder eine externe Bibliothek verwenden.
Es ist eine sehr komplizierte Situation, wenn Sie die asynchrone Erfassung von Daten in einer Schleife verwenden oder if-else-Bedingungen verwenden müssen.
Exponentieller Rollback-Mechanismus
Die Verwendung von Promise zur Implementierung der Rollback-Logik ist ziemlich umständlich
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) })
Der Code ist ein Kopfzerbrechen beim Ansehen , und Sie möchten keinen solchen Code sehen. Wir können dieses Beispiel mit async/await wiederholen, um es einfacher zu machen
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) } } }
Der obige Code sieht sehr komfortabel aus, nicht wahr
Zwischenwert
Nicht so beängstigend wie das vorherige Beispiel. Wenn Sie drei asynchrone Funktionen haben, die wiederum voneinander abhängen, müssen Sie aus mehreren hässlichen Lösungen auswählen.
FunktionA gibt ein Versprechen zurück, dann benötigt FunktionB diesen Wert und FunktionC benötigt die Werte, nachdem FunktionA und FunktionB abgeschlossen sind.
Option 1: dann Weihnachtsbaum
function executeAsyncTask () { return functionA() .then((valueA) => { return functionB(valueA) .then((valueB) => { return functionC(valueA, valueB) }) }) }
Mit dieser Lösung können wir dann WertA und WertB im dritten erhalten und dann das Gleiche wie zuvor tun zwei then Holen Sie sich die Werte von valueA und valueB. Sie können den Weihnachtsbaum hier nicht platt machen (die Hölle ruinieren), sonst verlieren Sie den Abschluss und valueA ist in functioinC nicht verfügbar.
Option 2: Zum Bereich der oberen Ebene wechseln
function executeAsyncTask () { let valueA return functionA() .then((v) => { valueA = v return functionB(valueA) }) .then((valueB) => { return functionC(valueA, valueB) }) }
In diesem Weihnachtsbaum verwenden wir den Retainer valueA für den höheren Bereich, da der Bereich valueA außerhalb aller Bereiche liegt, also FunktionC kann den Wert der ersten FunktionA-Vervollständigung erhalten.
Dies ist eine sehr „richtige“ Syntax zum Abflachen der .then-Kette. Auf diese Weise müssen wir jedoch zwei Variablen valueA und v verwenden, um denselben Wert zu halten.
Option 3: Verwenden Sie ein redundantes Array
function executeAsyncTask () { return functionA() .then(valueA => { return Promise.all([valueA, functionB(valueA)]) }) .then(([valueA, valueB]) => { return functionC(valueA, valueB) }) }
Verwenden Sie ein Array im Then von Funktion A, um valueA und Promise zusammen zurückzugeben, was den Weihnachtsbaum effektiv flach machen kann ( Rückruf Hölle).
Option 4: Schreiben Sie eine Hilfsfunktion
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))
Das ist machbar, schreiben Sie eine Hilfsfunktion, um die Kontextvariablendeklaration zu maskieren. Aber ein solcher Code ist sehr schwer zu lesen, insbesondere für Leute, die mit dieser Magie nicht vertraut sind.
Mit async/await verschwinden unsere Probleme auf magische Weise
async function executeAsyncTask () { const valueA = await functionA() const valueB = await functionB(valueA) return function3(valueA, valueB) }
Verwenden Sie async/await, um mehrere parallele Anfragen zu bearbeiten
Ähnlich wie oben, wenn Sie mehrere Anfragen gleichzeitig ausführen möchten Einmal asynchrone Aufgaben und die anschließende Verwendung ihrer Werte an verschiedenen Stellen können problemlos mit async/await erledigt werden.
async function executeParallelAsyncTasks () { const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ]) doSomethingWith(valueA) doSomethingElseWith(valueB) doAnotherThingWith(valueC) }
Array-Iterationsmethode
Sie können asynchrone Funktionen in Zuordnungs-, Filter- und Reduzierungsmethoden verwenden, obwohl sie nicht sehr intuitiv erscheinen. Sie können jedoch mit dem folgenden Code in der Konsole experimentieren.
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))
Lösung:
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ] [ 1, 2, 3, 4 ] 10
Wenn es sich um Karteniterationsdaten handelt, sehen Sie, dass der Rückgabewert [2, 4, 6, 8] ist. Das einzige Problem besteht darin, dass jeder Wert von der AsyncFunction-Funktion in ein Versprechen eingeschlossen wird
Wenn Sie also deren Werte erhalten möchten, müssen Sie das Array an Promise.All() übergeben, um das Promise zu entpacken.
main() .then(v => Promise.all(v)) .then(v => console.log(v)) .catch(err => console.error(err)) 一开始你会等待 Promise 解决,然后使用map遍历每个值 function main () { return Promise.all([1,2,3,4].map((value) => asyncThing(value))) } main() .then(values => values.map((value) => value * 2)) .then(v => console.log(v)) .catch(err => console.error(err))
Das scheint einfacher zu sein?
Die Async/Await-Version ist immer noch nützlich, wenn Sie eine synchrone Logik mit langer Laufzeit und eine andere asynchrone Aufgabe mit langer Laufzeit in Ihrem Iterator haben
这种方式当你能拿到第一个值,就可以开始做一些计算,而不必等到所有 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中如何实现百度搜索时的动态下拉框
Das obige ist der detaillierte Inhalt vonVerwenden von Async- und Await-Funktionen in Node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!