asap异步执行实现原理
目录
- 为什么分析asap
- asap概述
- asap源码解析—Node版
- 参考
1.为什么分析asap
在之前的文章 async和await是如何实现异步编程? 中的 “浅谈Promise如何实现异步执行” 小节,提到了 Promise 异步执行是通过 asap 这个库来实现的。所以为了进一步深入 Promise 异步执行的原理,深入分析一下 asap 是有必要的。
补充说明:这里提及的Promise并不是Node和浏览器的原生实现,是一个第三方库实现,仅以此为参考。
2.asap概述
asap 是 as soon as possible 的简称,在 Node 和浏览器环境下,能将回调函数以高优先级任务来执行(下一个事件循环之前),即把任务放在微任务队列中执行。
宏任务(macro-task)和微任务(micro-task)表示异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。
用法:
asap(function () {
// ...
});
3.asap源码解析—Node版
asap 源码库中包含了支持Node和浏览器的两个版本,这里主要进行分析Node版。
主要包含两个源码文件:
这两个文件分别导出了 asap 和 rawAsap 这两个方法,而 asap 可以看作是对 rawAsap 的进一步封装,通过缓存的 domain(可以捕捉处理 try catch 无法捕捉的异常,针对异步代码的异常处理)和 try/finally 实现了即使某个任务抛出异常也可以恢复任务栈的继续执行,另外也做了一点缓存优化(具体见源码)。
因此这里主要分析 raw.js 里面的代码即可:
1.首先是对外导出的 rawAsap 方法
var queue = [];
var flushing = false;
function rawAsap(task) {
if (!queue.length) {
requestFlush();
flushing = true;
}
queue[queue.length] = task;
}
源码解析:如果任务栈 queue 为空,则触发 requestFlush 方法,并将 flushing 标志为 true,并且始终会将要执行的 task 添加到任务栈 queue 的末尾。这里需要注意的是由于 requestFlush 中是异步去触发任务栈的执行的,所以即使queue[queue.length] = task在 requestFlush 调用之后执行,也能保证在任务栈 queue 真正执行前,任务 task 已经被添加到了任务栈 queue 的末尾。(如果任务栈 queue 不为空,说明 requestFlush 已经触发了,此时任务栈正在被循环依次执行,执行完毕会清空任务栈)
2.其次是异步触发 flush 方法执行的 requestFlush 方法
var domain;
var hasSetImmediate = typeof setImmediate === "function";
// 设置为 rawAsap 的属性,方便在任务执行异常时再次触发 requestFlush
rawAsap.requestFlush = requestFlush;
function requestFlush() {
// 确保 flushing 未绑定到任何域
var parentDomain = process.domain;
if (parentDomain) {
if (!domain) {
// 惰性加载执行 domain 模块
domain = require("domain");
}
domain.active = process.domain = null;
}
if (flushing && hasSetImmediate) {
setImmediate(flush);
} else {
process.nextTick(flush);
}
if (parentDomain) {
domain.active = process.domain = parentDomain;
}
}
源码解析:核心代码其实就一句:setImmediate(flush),通过 setImmediate 异步执行 flush 方法。而判断 parentDomain 以及设置和恢复 domain 都只是为了当前的 flush 方法不绑定任何域执行。而这里还有一个 hasSetImmediate 判断,是为了做兼容降级处理,如果不存在 setImmediate 方法,则使用 process.nextTick 方法触发异步执行。但使用 process.nextTick 方法有一个缺陷,就是它不能够处理递归。
3.最后是执行任务栈的 flush 方法
// 下一个任务在任务队列中执行的位置
var index = 0;
var capacity = 1024;
function flush() {
while (index < queue.length) {
var currentIndex = index;
// 在调用任务之前先设置下一个任务的索引,可以确保再次触发 flush 方法时,跳过异常任务
index = index + 1;
queue[currentIndex].call();
// 防止内存泄露
if (index > capacity) {
for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
queue[scan] = queue[scan + index];
}
queue.length -= index;
index = 0;
}
}
queue.length = 0;
index = 0;
flushing = false;
}
源码解析:通过 while 循环依次去执行任务栈 queue 中的每一个任务,这里需要注意一点,index + 1 表示下一个要执行的任务下标,而其放在 queue[currentIndex].call() 之前,是为了保证当当前任务执行发生异常了,再次触发 requestFlush 方法时,能够跳过发生异常的任务,从下一个任务开始执行。而判断 if (index > capacity) 是为了防止内存泄露,当任务栈 queue 的长度超过了指定的阈值 capacity 时,对任务栈 queue 中的任务进行移动,将所有剩余的未执行的任务置前,并重置任务栈 queue 的长度。当所有任务执行完毕后,重置任务栈以及相应状态。
4.总结
rawAsap 方法是通过 setImmediate 或 process.nextTick 来实现异步执行的任务栈,而 asap 方法是对 rawAsap 方法的进一步封装,通过缓存的 domain 和 try/finally 实现了即使某个任务抛出异常也可以恢复任务栈的继续执行(再次调用rawAsap.requestFlush)。
4.参考
asap - High-priority task queue for Node.js and browsers
asap异步执行实现原理的更多相关文章
- Async和Await异步编程的原理
1. 简介 从4.0版本开始.NET引入并行编程库,用户能够通过这个库快捷的开发并行计算和并行任务处理的程序.在4.5版本中.NET又引入了Async和Await两个新的关键字,在语言层面对并行编程给 ...
- Atitit.异步编程技术原理与实践attilax总结
Atitit.异步编程技术原理与实践attilax总结 1. 俩种实现模式 类库方式,以及语言方式,java futuretask ,c# await1 2. 事件(中断)机制1 3. Await 模 ...
- Python开发程序:RPC异步执行命令(RabbitMQ双向通信)
RPC异步执行命令 需求: 利用RibbitMQ进行数据交互 可以对多台服务器进行操作 执行命令后不等待命令的执行结果,而是直接让输入下一条命令,结果出来后自动打印 实现异步操作 不懂rpc的请移步h ...
- Spring源码学习之:@async 方法上添加该注解实现异步调用的原理
在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率.今天我们来探讨下 spring 是如何完成这个功能的. spring 在扫描be ...
- Vue(基础七)_webpack(webpack异步加载原理)
---恢复内容开始--- 一.前言 1.webpack异步加载原理’ 2.webpack.ensure原理 ...
- php异步执行其他程序
这里的“其他程序”,可能是linux命令,可能是其他的php文件. 网上说法有四种.分别为: 1.通过加载页面的时候通过ajax技术异步请求服务器 2.通过popen()函数 3.通过curl扩展 4 ...
- C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较
使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和 ...
- php 异步执行脚本
这里说的异步执行是让php脚本在后台挂起一个执行具体操作的脚本,主脚本退出后,挂起的脚本还能继续执行.比如执行某些耗时操作或可以并行执行的操作,可以采用php异步执行的方式.主脚本和子脚本的通讯可以采 ...
- PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)
源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...
随机推荐
- 数据库开发 Oracle与mysql间的批量处理接口 SSIS+存储过程实现
公司目前不同的业务系统用了不同的数据库,涉及到oracle.mysql.sqlserver.而一些核心的业务在mysql中,所以平时经常要把oracle.sqlserver中的数据插入到mysql中. ...
- django 和 七牛云 交互
django 和 七牛云 交互 七牛开发文档 安装 pip install qiniu 初始化 # access_key 个人中心的 ak # secret_key 个人中心的 sk from qin ...
- Jenkins下构建UI自动化之初体验
一.缘 起 笔者之前一直在Windows环境下编写UI自动化测试脚本,近日在看<京东系统质量保障技术实战>一书中,萌生出在jenkins下构建UI自动化测试的想法 二.思 路 首先,在Li ...
- 2. weddriver的定位方法
一. find_element_by_****的方式 首页在网页上鼠标右键选择检查并点击,查看需要定位的元素. https://www.baidu.com 以百度为例 导入模块的: from sel ...
- django缓存和跨域解决和短信验证码的使用
缓存 在实际项目中,存在大量的数据检索,比如我们刷微博的时候,刚开始加载速度慢一点,然后第一次加载完毕之后,如果你此时的手机没有网络,但是你发现你的微博还是可以照样刷,但是刷到一定的页面就走不动了,那 ...
- Bootstrap 基本配置与应用
配置使用 下载文件引用 下载方式:Bootstrap官网 https://www.bootcss.com/ 引用 例: <head> <meta charset="utf- ...
- 使用TensorFlow v2张量的一个简单的“hello world”示例
使用TensorFlow v2张量的一个简单的"hello world"示例 import tensorflow as tf # 创建一个张量 hello = tf.constan ...
- 044.集群存储-StorageClass
一 StoragClass 1.1 StorageClass概述 StorageClass作为对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节,一方面减少了用户对于存储资源细节的关注,另 ...
- Web Scraper 性能测试 (-_-)
刚在研究 Python 爬虫的时候,看到了个小白工具,叫 Web Scraper,于是来测试下好不好用. Web Scraper 是什么? 它是一个谷歌浏览器的插件, 用于批量抓取网页信息, 主要特点 ...
- API开放平台接口设计-------基于OAuth2.0协议方式
1,简介OAuth http://www.ruanyifeng.com/blog/2019/04/oauth_design.html OAuth 是什么? http://www.ruanyifeng. ...