Cet article présente principalement l'introduction à la programmation asynchrone JS, qui a une certaine valeur de référence. Maintenant, je le partage avec tout le monde. Les amis dans le besoin peuvent s'y référer
1.1 Ce qui est asynchrone
异步(async)
est relatif à 同步(sync)
et est facile à comprendre.
同步
C'est l'exécution d'une chose à la fois. La tâche suivante ne peut être exécutée qu'après l'exécution de la tâche précédente. Et 异步
, par exemple :
setTimeout(function cbFn(){ console.log('learnInPro'); }, 1000);console.log('sync things');
setTimeout est un 异步任务
Lorsque le moteur JS exécute séquentiellement setTimeout et découvre qu'il s'agit d'une tâche asynchrone, il suspend la tâche et continue d'exécuter la tâche. code ultérieur. La fonction de rappel cbFn ne sera exécutée que 1000 ms plus tard. Lorsque setTimeout est exécuté, JS n'attendra pas bêtement 1000 ms pour exécuter la fonction de rappel cbFn, mais continuera à exécuter le code suivant.
1.2 Pourquoi utiliser l'asynchrone en JS
Puisque javascript est 单线程
, il ne peut s'exécuter que sur le thread principal du moteur JS, donc le code js Il peut ne peut être exécuté que ligne par ligne, et plusieurs tâches de code js ne peuvent pas être exécutées en même temps. Cela a pour conséquence que s'il y a un long calcul, ou une opération IO telle qu'une requête ajax, s'il n'y a pas d'asynchrone, l'utilisateur le fera. apparaîtra. Attendez longtemps et comme la tâche en cours n'est pas terminée, toutes les autres opérations ne répondront plus à ce moment-là.
1.3 Alors pourquoi JS n'est-il pas conçu pour être multithread
Ceci est principalement lié à l'histoire de javascript qui a été initialement conçu pour gérer certaines validations de formulaires. et les opérations DOM. Il a été créé, il adopte donc principalement le modèle 单线程
pour la légèreté et la simplicité du langage. 多线程模型
est beaucoup plus compliqué que 单线程
Par exemple, le multithreading doit gérer le partage des ressources entre les threads, et doit également résoudre des problèmes tels que la synchronisation des états.
Si JS est multithread, lorsque vous souhaitez effectuer l'opération d'insertion d'un DOM dans p, et qu'un autre thread effectue l'opération de suppression du p, de nombreux problèmes surviendront à ce moment-là. mécanisme de verrouillage, etc. à cet effet.
D'accord, nous savons maintenant que JS monothread utilisera un traitement asynchrone afin d'éviter une longue attente. Par exemple, lors de l'exécution d'une opération ajax, lorsque js envoie une requête, il n'attendra pas le retour des données du serveur, mais continuera à effectuer les tâches suivantes et attendra le retour des données du serveur avant de demander au moteur js de les traiter. .
Alors, quels sont les modèles asynchrones courants ?
Fonction de rappel
Écoute d'événements
Mode publication/abonnement (également appelé mode observateur )
promesse
Plus tard dans ES6, la fonction
Generator
a été introduite dans ES7,async/await
a même introduit la programmation asynchrone dans un une toute nouvelle étape.
Nous discuterons de ces modes asynchrones en détail plus tard, mais nous n'avons ici qu'un concept.
1.4 Comment JS implémente-t-il les opérations asynchrones
Comment JS implémente-t-il spécifiquement les opérations asynchrones ?
La réponse est JS的事件循环机制(Event Loop)
.
具体来说:
Lorsque JS sera analysé et exécuté, il sera divisé en deux types de tâches par le moteur, 同步任务(synchronous)
et 异步任务(asynchronous)
.
Pour les tâches synchrones, elles seront poussées vers la pile d'exécution pour exécuter ces tâches dans l'ordre.
Pour une tâche asynchrone, lorsqu'elle pourra être exécutée, elle sera placée dans un 任务队列(task queue)
et attendra que le moteur JS s'exécute.
Lorsque toutes les tâches de synchronisation dans la pile d'exécution sont terminées, le moteur JS ira dans la file d'attente des tâches pour vérifier s'il y a une tâche et placera la tâche dans la pile d'exécution pour exécution. accédez à nouveau à la file d’attente des tâches. Vérifiez s’il existe des tâches qui peuvent déjà être exécutées. Ce mécanisme de vérification de boucle est appelé 事件循环(Event Loop)
.
对于任务队列
,其实是有更细的分类。其被分为 微任务(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中文网!
相关推荐:
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!