Heim  >  Artikel  >  Web-Frontend  >  Verwenden von Async- und Await-Funktionen in Node.js

Verwenden von Async- und Await-Funktionen in Node.js

亚连
亚连Original
2018-06-05 14:49:352603Durchsuche

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(&#39;Waiting&#39;, timeout, &#39;ms&#39;)
  await wait(timeout)
  console.log(&#39;Retrying&#39;, 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+的版本不妨尝试一下,或许会有新的收获。

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

相关文章:

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

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

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

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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn