This article mainly introduces the introduction to JS asynchronous programming, which has certain reference value. Now I share it with everyone. Friends in need can refer to it
1.1 What is asynchronous
Asynchronous (async)
is relative to Sync (sync)
, easy to understand.
Synchronization
is the execution of one thing. The next task can only be executed after the previous task has been executed. And Asynchronous
For example:
setTimeout(function cbFn(){ console.log('learnInPro'); }, 1000);console.log('sync things');
setTimeout is an asynchronous task
. When the JS engine sequentially executes setTimeout and finds that it is an asynchronous task, it will suspend the task. Start and continue executing the following code. The callback function cbFn will not be executed until 1000ms later. This is asynchronous. When setTimeout is executed, JS will not wait stupidly for 1000ms to execute the cbFn callback function, but will continue to execute the following code.
1.2 Why use asynchronous in JS
Since javascript is single-threaded
, it can only run on the main thread of the JS engine , so js code can only be executed line by line, and multiple js code tasks cannot be executed at the same time. This results in that if there is a long calculation, or an IO operation such as an ajax request, if there is no asynchronous , the user will wait for a long time, and because the current task has not been completed, all other operations will be unresponsive at this time.
1.3 Then why is JS not designed to be multi-threaded?
This is mainly related to the history of javascript. js was originally used to handle some form validation and DOM operations. was created, so it adopts the single-threaded
mode mainly for the lightweight and simplicity of the language. Multi-threaded model
is much more complicated than single-threaded
. For example, multi-threading needs to deal with the sharing of resources between threads, and also needs to solve issues such as state synchronization.
If JS is multi-threaded, when you want to perform the operation of inserting a DOM into p, and another thread performs the operation of deleting the p, many problems will occur at this time. We also need Add lock mechanism etc. for this purpose.
Okay, so now we know that single-threaded JS will use asynchronous processing in order to avoid long-term waiting. For example, when performing an ajax operation, when js sends a request, it will not wait for the server data to return, but will continue to perform subsequent tasks, and wait until the server data returns before notifying the js engine to process it.
So what are the common asynchronous patterns?
Callback function
Event listening
Publish/subscribe mode (also known as observer mode )
promise
Later in ES6, the
Generator
function was introduced; in ES7,async/await
brings asynchronous programming into a new stage.
We will discuss these asynchronous modes in detail later, but here we have a concept.
1.4 How does JS implement asynchronous operations
How does JS implement asynchronous operations?
The answer is JS’s event loop mechanism (Event Loop)
.
Specifically:
When JS is parsed and executed, it will be divided into two types of tasks by the engine, Synchronous tasks (synchronous)
and Asynchronous task (asynchronous)
.
For synchronous tasks, they will be pushed to the execution stack to execute these tasks in order.
For an asynchronous task, when it can be executed, it will be placed in a task queue (task queue)
to wait for the JS engine to execute.
When all synchronization tasks in the execution stack are completed, the JS engine will go to the task queue to check whether there is a task, and put the task into the execution stack for execution. After execution, it will go to the task queue again. Check to see if there are any tasks that can already be executed. This loop checking mechanism is called 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中文网!
相关推荐:
The above is the detailed content of Introduction to JS asynchronous programming. For more information, please follow other related articles on the PHP Chinese website!