Heim >Web-Frontend >js-Tutorial >So optimieren Sie die asynchrone js-Funktion
Dieses Mal zeige ich Ihnen, wie Sie die js-asynchrone Funktion optimieren. Was sind die Vorsichtsmaßnahmen für die Optimierung der js-asynchronen Funktion?
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 ist eine gute Sache, um die Callback-Hölle zu lösen und asynchrone Prozesse klarer zu machen.
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) })
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 }
Die beiden Methoden sind in der Anwendung genau gleich. Sie können sie dann 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()
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
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
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 in Promise ein Promise zurückgegeben wird, 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
Wenn wir in Resolve eine Ablehnung übergeben, können wir sie direkt mit „catch“ extern ü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) }
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
, also sei so Verwenden Sie es unbedingt. Denken Sie daran, dass das Schlüsselwort „await“
let number = await getNumber() console.log(number) // 1
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
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.
Mehrere unabhängige asynchrone Funktionsaufrufe zusammenführen
Wenn wir jetzt den Avatar und die Benutzerdetails eines Benutzers abrufen möchten (und dies sind zwei Schnittstellen, obwohl sie unter normalen Umständen selten auftreten)
async function getUser () { let avatar = await getAvatar() let userInfo = await getUserInfo() return { avatar, userInfo } }
这样的代码就造成了一个问题,我们获取用户信息的接口并不依赖于头像接口的返回值。
但是这样的代码却会在获取到头像以后才会去发送获取用户信息的请求。
所以我们对这种代码可以这样处理:
async function getUser () { let [avatar, userInfo] = await Promise.all([getAvatar(), getUserInfo()]) return { avatar, userInfo } }
这样的修改就会让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()
这样的执行好像并没有什么问题,我们也会得到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) }
这样的代码运行,会在拿到uid: 1的数据后才会去请求uid: 2的数据
--------------------------------------------------------------------------------
关于这两种问题的解决方案:
目前最优的就是将其替换为map结合着Promise.all来实现:
await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))
这样的代码实现会同时实例化三个Promise,并请求getUserInfo
P.S. 草案中有一个await*,可以省去Promise.all
await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))
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)); }
co是帮助我们添加了Promise.all的处理的(膜拜TJ大佬)。
总结
总结一下关于async函数编写的几个小提示:
1.使用return Promise.reject()在async函数中抛出异常
2.让相互之间没有依赖关系的异步函数同时执行
3.不要在循环的回调中/for、while循环中使用await,用map来代替它
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
Das obige ist der detaillierte Inhalt vonSo optimieren Sie die asynchrone js-Funktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!