Heim > Artikel > Web-Frontend > Einführung in die asynchrone JS-Programmierung
Dieser Artikel stellt hauptsächlich die Einführung in die asynchrone JS-Programmierung vor, die einen gewissen Referenzwert hat. Jetzt kann ich sie mit allen teilen. Freunde in Not können sich darauf beziehen
1.1 Was asynchron ist
异步(async)
ist relativ zu 同步(sync)
und leicht zu verstehen.
同步
Es ist die Ausführung einer Sache nach der anderen. Die nächste Aufgabe kann erst ausgeführt werden, nachdem die vorherige Aufgabe ausgeführt wurde. Und 异步
zum Beispiel:
setTimeout(function cbFn(){ console.log('learnInPro'); }, 1000);console.log('sync things');
setTimeout ist ein 异步任务
Wenn die JS-Engine nacheinander setTimeout ausführt und feststellt, dass es sich um eine asynchrone Aufgabe handelt, wird sie die Aufgabe anhalten und den folgenden Code weiter ausführen. Die Rückruffunktion cbFn wird erst 1000 ms später ausgeführt. Wenn setTimeout ausgeführt wird, wartet JS nicht dummerweise 1000 ms, um die cbFn-Rückruffunktion auszuführen, sondern führt weiterhin den folgenden Code aus.
1.2 Warum asynchron in JS verwenden
Da Javascript 单线程
ist, kann es nur im Hauptthread der JS-Engine ausgeführt werden, sodass der JS-Code dies kann kann nur zeilenweise ausgeführt werden, und mehrere JS-Code-Aufgaben können nicht gleichzeitig ausgeführt werden. Dies führt dazu, dass der Benutzer dies tut, wenn eine lange Berechnung oder eine E/A-Operation wie eine Ajax-Anfrage vorliegt Warten Sie lange, und da die aktuelle Aufgabe noch nicht abgeschlossen ist, reagieren alle anderen Vorgänge zu diesem Zeitpunkt nicht mehr.
1.3 Warum ist JS dann nicht für Multithreading konzipiert?
Dies hängt hauptsächlich mit der Geschichte von Javascript zusammen, das ursprünglich für die Formularvalidierung entwickelt wurde und DOM-Operationen wurden erstellt und übernehmen daher hauptsächlich das 单线程
-Muster für die Leichtigkeit und Einfachheit der Sprache. 多线程模型
ist viel komplizierter als 单线程
. Multithreading muss sich beispielsweise mit der gemeinsamen Nutzung von Ressourcen zwischen Threads befassen und auch Probleme wie die Statussynchronisierung lösen.
Wenn JS Multithreading ist und Sie den Vorgang des Einfügens eines DOM in p ausführen möchten und ein anderer Thread den Vorgang des Löschens von p ausführt, treten zu diesem Zeitpunkt viele Probleme auf. Wir müssen auch hinzufügen Zu diesem Zweck ist ein Schließmechanismus usw. erforderlich.
Okay, jetzt wissen wir, dass Single-Threaded JS asynchrone Verarbeitung verwendet, um langes Warten zu vermeiden. Wenn js beispielsweise eine Ajax-Operation ausführt, wartet es nicht nur auf die Rückkehr der Serverdaten, sondern führt auch weiterhin nachfolgende Aufgaben aus und wartet, bis die Serverdaten zurückgegeben werden, bevor es die js-Engine zur Verarbeitung benachrichtigt Es.
Was sind also die häufigsten asynchronen Muster?
Rückruffunktion
Ereignisüberwachung
Publish/Subscribe-Modus (auch als Beobachtermodus bekannt). )
Versprechen
Später in ES6 wurde die
Generator
-Funktion eingeführt; in ES7 wurdeasync/await
sogar asynchrone Programmierung eingeführt ganz neue Bühne.
Wir werden diese asynchronen Modi später ausführlich besprechen, aber wir haben hier nur ein Konzept.
1.4 Wie implementiert JS asynchrone Operationen
Wie implementiert JS asynchrone Operationen?
Die Antwort ist JS的事件循环机制(Event Loop)
.
具体来说:
Wenn JS analysiert und ausgeführt wird, wird es von der Engine in zwei Arten von Aufgaben unterteilt: 同步任务(synchronous)
und 异步任务(asynchronous)
.
Bei synchronen Aufgaben werden sie auf den Ausführungsstapel verschoben, um diese Aufgaben der Reihe nach auszuführen.
Wenn eine asynchrone Aufgabe ausgeführt werden kann, wird sie in einem 任务队列(task queue)
abgelegt und wartet auf die Ausführung durch die JS-Engine.
Wenn alle Synchronisierungsaufgaben im Ausführungsstapel abgeschlossen sind, geht die JS-Engine in die Aufgabenwarteschlange, um zu sehen, ob eine Aufgabe vorhanden ist, und stellt die Aufgabe zur Ausführung in den Ausführungsstapel Gehen Sie erneut zur Aufgabenwarteschlange, um festzustellen, ob bereits Aufgaben ausgeführt werden können. Dieser Schleifenprüfmechanismus wird 事件循环(Event Loop)
genannt.
对于任务队列
,其实是有更细的分类。其被分为 微任务(microtask)队列
& 宏任务(macrotask)队列
宏任务: setTimeout、setInterval等,会被放在宏任务(macrotask)队列。
微任务: Promise的then、Mutation Observer等,会被放在微任务(microtask)队列。
Event Loop的执行顺序是:
首先执行执行栈里的任务。
执行栈清空后,检查微任务(microtask)队列,将可执行的微任务全部执行。
取宏任务(macrotask)队列中的第一项执行。
回到第二步。
注意: 微任务队列每次全执行,宏任务队列每次只取一项执行。
我们举个例子:
setTimeout(() => { console.log('我是第一个宏任务'); Promise.resolve().then(() => { console.log('我是第一个宏任务里的第一个微任务'); }); Promise.resolve().then(() => { console.log('我是第一个宏任务里的第二个微任务'); }); }, 0); setTimeout(() => { console.log('我是第二个宏任务'); }, 0); Promise.resolve().then(() => { console.log('我是第一个微任务'); });console.log('执行同步任务');
最后的执行结果是:
// 执行同步任务
// 我是第一个微任务
// 我是第一个宏任务
// 我是第一个宏任务里的第一个微任务
// 我是第一个宏任务里的第二个微任务
// 我是第二个宏任务
1.5 JS异步编程模式
这里我们已经知道了JS中异步的运行机制,我们翻回头来详细的了解一下常见的各种异步的编程模式。
回调函数
事件监听
发布/订阅模式
Promise
Generator
async/await
1.5.1 回调函数
回调函数是异步操作最基本的方法。
比如:我有一个异步操作(asyncFn),和一个同步操作(normalFn)。
function asyncFn() { setTimeout(() => { console.log('asyncFn'); }, 0) }function normalFn() { console.log('normalFn'); } asyncFn(); normalFn();// normalFn// asyncFn
如果按照正常的JS处理机制来说,同步操作一定发生在异步之前。如果我想要将顺序改变,最简单的方式就是使用回调的方式处理。
function asyncFn(callback) { setTimeout(() => { console.log('asyncFn'); callback(); }, 0) }function normalFn() { console.log('normalFn'); } asyncFn(normalFn);// asyncFn// normalFn
1.5.2 事件监听
另一种思路是采用事件驱动模式。这种思路是说异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
比如一个我们注册一个按钮的点击事件或者注册一个自定义事件,然后通过点击或者trigger的方式触发这个事件。
1.5.3 发布/订阅模式(又称观察者模式)
这个重点讲下,发布/订阅模式像是事件监听模式的升级版。
在发布/订阅模式中,你可以想象存在一个消息中心的地方,你可以在那里“注册一条消息”,那么被注册的这条消息可以被感兴趣的若干人“订阅”,一旦未来这条“消息被发布”,则所有订阅了这条消息的人都会得到提醒。
这个就是发布/订阅模式的设计思路。接下来我们一点一点实现一个简单的发布/订阅模式。
首先我们先实现一个消息中心。
// 先实现一个消息中心的构造函数,用来创建一个消息中心function MessageCenter(){ var _messages = {}; // 所有注册的消息都存在这里 this.regist = function(){}; // 用来注册消息的方法 this.subscribe = function(){}; // 用来订阅消息的方法 this.fire = function(){}; // 用来发布消息的方法}
这里一个消息中心的雏形就创建好了,接下来我们只要完善下regist,subscribe和fire这三个方法就好了。
function MessageCenter(){ var _messages = {}; // 对于regist方法,它只负责注册消息,就只接收一个注册消息的类型(标识)参数就好了。 this.regist = function(msgType){ // 判断是否重复注册 if(typeof _messages[msgType] === 'undefined'){ _messages[msgType] = []; // 数组中会存放订阅者 }else{ console.log('这个消息已经注册过了'); } } // 对于subscribe方法,需要订阅者和已经注册了的消息进行绑定 // 由于订阅者得到消息后需要处理消息,所以他是一个个的函数 this.subscribe = function(msgType, subFn){ // 判断是否有这个消息 if(typeof _messages[msgType] !== 'undefined'){ _messages[msgType].push(subFn); }else{ console.log('这个消息还没注册过,无法订阅') } } // 最后我们实现下fire这个方法,就是去发布某条消息,并通知订阅这条消息的所有订阅者函数 this.fire = function(msgType, args){ // msgType是消息类型或者说是消息标识,而args可以设置这条消息的附加信息 // 还是发布消息时,判断下有没有这条消息 if(typeof _messages[msgType] === 'undefined') { console.log('没有这条消息,无法发布'); return false; } var events = { type: msgType, args: args || {} }; _messages[msgType].forEach(function(sub){ sub(events); }) } }
这样,一个简单的发布/订阅模式就完成了,当然这只是这种模式的其中一种简单实现,还有很多其他的实现方式。
就此我们就可以用他来处理一些异步操作了。
var msgCenter = new MessageCenter(); msgCenter.regist('A'); msgCenter.subscribe('A', subscribeFn);function subscribeFn(events) { console.log(events.type, events.args); } // -----setTimeout(function(){ msgCenter.fire('A', 'fire msg'); }, 1000);// A, fire msg
我们在这篇文章里深入讲解了什么是异步,为什么要有异步以及在JS中引擎是如何处理异步的,后面我们讲解了几种异步编程模式并重点讲了下发布/订阅模式,
在下一章里面我们重点把另外几种异步编程模式Promise,Generator,async/await讲完。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
Das obige ist der detaillierte Inhalt vonEinführung in die asynchrone JS-Programmierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!