Heim > Web-Frontend > js-Tutorial > Detaillierte Erläuterung der Schritte zum Schreiben der asynchronen js-Funktion

Detaillierte Erläuterung der Schritte zum Schreiben der asynchronen js-Funktion

php中世界最好的语言
Freigeben: 2018-05-22 09:49:27
Original
2028 Leute haben es durchsucht

Dieses Mal werde ich Ihnen die Schritte zum Schreiben von js-asynchronen Funktionen ausführlich erläutern. Was sind die Vorsichtsmaßnahmen zum Schreiben von js-asynchronen Funktionen?

Der Mai 2018 ist da und auch die Wartung der 4.x-Version von Node wurde eingestellt. Ein bestimmter Dienst unseres Unternehmens wurde ebenfalls auf 8.x umgestellt. Wir migrieren derzeit den vorherigen Generator auf koa2.x . Ersetzen Sie alle durch Async. Während des Ersetzungsprozesses haben wir jedoch festgestellt, dass durch den Missbrauch von Async Zeit verloren geht async

Zunächst müssen Sie Promise verstehen

Promise ist die Grundlage für die Verwendung von async/await, daher müssen Sie zunächst verstehen, was Promise tut

Versprechen hilft dabei, die Callback-Hölle zu lösen. Gute Sache, macht asynchrone Prozesse klarer.

Ein einfaches Beispiel für die Konvertierung von Error-first-callback in Promise:

const fs = require('fs')
function readFile (fileName) {
 return new Promise((resolve, reject) => {
  fs.readFile(fileName, (err, data) => {
   if (err) reject(err)
   resolve(data)
  })
 })
}
readFile('test.log').then(data => {
 console.log('get data')
}, err => {
 console.error(err)
})
Nach dem Login kopieren

Wir rufen die Funktion auf, um eine Promise-Instanz zurückzugeben, und lesen die Datei während des Instanziierungsprozesses Der Rückruf wird ausgelöst, um den Promise-Status zu ändern. Wir verwenden dann, um Änderungen im gelösten oder abgelehnten Status zu überwachen. Der erste Rückruf dient der Auflösungsverarbeitung und der zweite Rückruf dient der Ablehnungsverarbeitung.

Die Beziehung zwischen Async und Promise

Die Async-Funktion entspricht einer Kurzfunktion, die eine Promise-Instanz zurückgibt. Der Effekt ist wie folgt :

function getNumber () {
 return new Promise((resolve, reject) => {
  resolve(1)
 })
}
// =>
async function getNumber () {
 return 1
}
Nach dem Login kopieren

Beide werden auf genau die gleiche Weise verwendet. Sie können then verwenden, um den Rückgabewert nach dem Aufruf der getNumber-Funktion zu überwachen. Und wie man die Async-Await-Syntax verwendet: Die Ausführung von

getNumber().then(data => {
 // got data
})
// =>
let data = await getNumber()
Nach dem Login kopieren

await erhält das Promise-Ausführungsergebnis hinter dem -Ausdruck , was unserem Aufruf von then entspricht, um das Rückrufergebnis zu erhalten . P.S. Als die Async/Await-Unterstützung nicht sehr hoch war, würde sich jeder dafür entscheiden, Generator/Yield in Kombination mit einigen Co-ähnlichen Bibliotheken zu verwenden, um ähnliche Effekte zu erzielen.

Die Ausführung des Async-Funktionscodes erfolgt synchron und das Ergebnis wird zurückgegeben asynchron

Es ist sehr wichtig, dass die asynchrone Funktion immer eine Promise-Instanz zurückgibt. Wenn Sie also eine asynchrone Funktion aufrufen, können Sie verstehen, dass sich der darin enthaltene Code in einem neuen Promise befindet und daher synchron ausgeführt wird. Die endgültige Rückgabe Die Operation entspricht dem Aufruf von „resolution“ im Versprechen:

async function getNumber () {
 console.log('call getNumber()')
 return 1
}
getNumber().then(_ => console.log('resolved'))
console.log('done')
// 输出顺序:
// call getNumber()
// done
// resolved
Nach dem Login kopieren

Das Versprechen innerhalb des Versprechens wird verdaut

das heißt, wenn wir haben den folgenden Code:

function getNumber () {
 return new Promise(resolve => {
  resolve(Promise.resolve(1))
 })
}
getNumber().then(data => console.log(data)) // 1
Nach dem Login kopieren

Wenn wir dem oben Gesagten folgen, sollten die Daten, die wir dann erhalten, der Wert sein, der an „resolve“ übergeben wird, was eine weitere Promise-Instanz ist.
Tatsächlich erhalten wir jedoch den Rückgabewert direkt: 1. Das heißt, wenn wir ein Promise in Promise zurückgeben, hilft uns das Programm tatsächlich bei der Ausführung dieses Promises und löst dann einen Rückruf aus, wenn sich der interne Promise-Status ändert .
Eine interessante Sache:

function getNumber () {
 return new Promise(resolve => {
  resolve(Promise.reject(new Error('Test')))
 })
}
getNumber().catch(err => console.error(err)) // Error: Test
Nach dem Login kopieren

Wenn wir eine Ablehnung in Resolution übergeben, können wir Catch direkt verwenden, um es extern zu überwachen.
Diese Methode wird oft verwendet, um Ausnahmen in asynchronen Funktionen auszulösen So lösen Sie Ausnahmen in asynchronen Funktionen aus:

async function getNumber () {
 return Promise.reject(new Error('Test'))
}
try {
 let number = await getNumber()
} catch (e) {
 console.error(e)
}
Nach dem Login kopieren

Achten Sie darauf, die Schlüsselwörter „await“ nicht zu vergessen

Wenn Sie vergessen, das Schlüsselwort „await“ hinzuzufügen, wird auf Codeebene kein Fehler gemeldet, aber der Rückgabewert, den wir erhalten, ist ein Versprechen

let number = getNumber()
console.log(number) // Promise
Nach dem Login kopieren

, also sei so Verwenden Sie es unbedingt. Denken Sie daran, dass das Schlüsselwort „await“

let number = await getNumber()
console.log(number) // 1
Nach dem Login kopieren
Nach dem Login kopieren

nicht überall hinzugefügt werden muss.

Während der Ausführung des Codes müssen manchmal nicht alle asynchronen Vorgänge „await“ hinzufügen. Zum Beispiel die folgende Dateioperation:

Wir gehen davon aus, dass alle fs-APIs von uns in Promise-Versionen konvertiert wurden

let number = await getNumber()
console.log(number) // 1
Nach dem Login kopieren
Nach dem Login kopieren

Wir öffnen eine Datei über „await“ und schreiben die Datei dann zweimal.

Beachten Sie jedoch, dass wir das Schlüsselwort „await“ nicht vor den beiden Dateischreibvorgängen hinzugefügt haben.
Da dies überflüssig ist, müssen wir der API nur mitteilen, dass ich eine Textzeile in diese Datei schreiben möchte. Die Reihenfolge wird natürlich von fs gesteuert.
Dann verwenden wir „await“, um die Datei am Ende zu schließen .
Denn wenn wir den obigen Schreibvorgang ausführen, wird der Close-Callback nicht ausgelöst.
Mit anderen Worten: Das Auslösen des Callbacks bedeutet, dass die beiden oben genannten Schreibschritte abgeschlossen sind.

合并多个不相干的async函数调用

如果我们现在要获取一个用户的头像和用户的详细信息(而这是两个接口 虽说一般情况下不太会出现)

async function getUser () {
 let avatar = await getAvatar()
 let userInfo = await getUserInfo()
 return {
  avatar,
  userInfo
 }
}
Nach dem Login kopieren

这样的代码就造成了一个问题,我们获取用户信息的接口并不依赖于头像接口的返回值。
 但是这样的代码却会在获取到头像以后才会去发送获取用户信息的请求。
 所以我们对这种代码可以这样处理:

async function getUser () {
 let [avatar, userInfo] = await Promise.all([getAvatar(), getUserInfo()])
 return {
  avatar,
  userInfo
 }
}
Nach dem Login kopieren

这样的修改就会让getAvatar与getUserInfo内部的代码同时执行,同时发送两个请求,在外层通过包一层Promise.all来确保两者都返回结果。

让相互没有依赖关系的异步函数同时执行

一些循环中的注意事项

forEach

当我们调用这样的代码时:

async function getUsersInfo () {
 [1, 2, 3].forEach(async uid => {
  console.log(await getUserInfo(uid))
 })
}
function getuserInfo (uid) {
 return new Promise(resolve => {
  setTimeout(_ => resolve(uid), 1000)
 })
}
await getUsersInfo()
Nach dem Login kopieren

这样的执行好像并没有什么问题,我们也会得到1、2、3三条log的输出,但是当我们在await getUsersInfo()下边再添加一条console.log('done')的话,就会发现:

 我们会先得到done,然后才是三条uid的log,也就是说,getUsersInfo返回结果时,其实内部Promise并没有执行完。
 这是因为forEach并不会关心回调函数的返回值是什么,它只是运行回调。

不要在普通的for、while循环中使用await

使用普通的for、while循环会导致程序变为串行:

for (let uid of [1, 2, 3]) {
 let result = await getUserInfo(uid)
}
Nach dem Login kopieren

这样的代码运行,会在拿到uid: 1的数据后才会去请求uid: 2的数据

--------------------------------------------------------------------------------

关于这两种问题的解决方案:

目前最优的就是将其替换为map结合着Promise.all来实现:

await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))
Nach dem Login kopieren
Nach dem Login kopieren

这样的代码实现会同时实例化三个Promise,并请求getUserInfo

P.S. 草案中有一个await*,可以省去Promise.all

await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))
Nach dem Login kopieren
Nach dem Login kopieren

P.S. 为什么在使用Generator+co时没有这个问题

在使用koa1.x的时候,我们直接写yield [].map是不会出现上述所说的串行问题的看过co源码的小伙伴应该都明白,里边有这么两个函数(删除了其余不相关的代码):

function toPromise(obj) {
 if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
 return obj;
}
function arrayToPromise(obj) {
 return Promise.all(obj.map(toPromise, this));
}
Nach dem Login kopieren

co是帮助我们添加了Promise.all的处理的(膜拜TJ大佬)。

总结

总结一下关于async函数编写的几个小提示:

1.使用return Promise.reject()在async函数中抛出异常
2.让相互之间没有依赖关系的异步函数同时执行
3.不要在循环的回调中/for、while循环中使用await,用map来代替它

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

js中async函数使用方法详解

node搭建服务器,写接口,调接口,跨域方法详解

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Schritte zum Schreiben der asynchronen js-Funktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage