异步编程与浏览器执行模型
DESCRIPTION
someone's pptxTRANSCRIPT
Summary 异步的概念 浏览器中的异步编程 浏览器执行模型 异步的逻辑衔接模型 一些最佳实践 回调维护的工具
什么是异步异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
不靠谱的百度百科
什么是异步
异步与并发
打开电脑
写函数 A 编译
休息 写函数 B
编译 休息
编译通过
git push
单一工作单元情况下可异步,但效率不会提高
异步与并发组员 A
打开电脑
写函数 A
编译
git push
组员 B
打开电脑
写函数 B
编译
git push
多个工作单元可并发,提高效率
浏览器中的异步 事件
load / mouse* / key* / click / … 定时
setTimeout / setInterval 资源
<script> / <img> / <iframe> / XHR / … 其他
requestAnimationFrame / postMessage / …
浏览器中的异步var start = new Date;var i = 0;setTimeout(function() { alert(i); }, 2000);while (new Date - start < 5000) { // sleep}i = 1;
浏览器执行模型领任
务
干活
领任务干活
领任务
干活
浏览器执行模型 Event Loop – 事件循环
一个浏览上下文只会有一个 Event Loop 多个浏览上下文可共享一个 Event Loop
Task Queue – 任务队列 一个 Event Loop 可有多个 Task Queue
Task Source – 任务来源 来源相同的任务肯定在同一个 Task Queue 中
Task – 任务
浏览器执行模型浏览上下文
Event Loop
Task
Task Sourcen…1
1…nTask Queue
1…n
处理模型
Event
Loop
取得最后一个Task
执行Task
移除该Task
更新用户界面
处理模型while (true) { var taskQueue = getAppropriateTaskQueue(); var task = taskQueue.peek(); runTask(task); taskQueue.dequeue(); // 细节事项 updateUI();}
处理模型 – 取得最后一个 Task
更新 DOM
• 20ms
计算公式
• 400ms
解析脚本
• 200ms
渲染图像
• 50ms
键盘输入
处理模型 – 取得最后一个 Task
Task Queue I
Task Queue II
Task Queue III
我这任务很重要
我这任务很多
我会暖床……
处理模型 – 取得最后一个 Task 可由任意算法选择一个 Task Queue ,以控制
任务的优先级
鼠标 / 键盘响应 – 3 倍优先级
事件处理 / 脚本解析 / 函数回调
处理模型 – 执行 Task
Task Queue
我是定时任务• 开始于
• 16:45• 延迟
• 15 分钟• 执行的内容
• 代码……• 计时器 ID
• 10086
1 6 : 5 0
处理模型 – 执行 Task spin – 当一个任务的先决条件不满足时:
保存当前任务的 Task Source 停止当前任务 – 事件循环执行下一个任务 异步监控任务的先决条件直到条件满足 使用保存的 Task Source ,将任务放回队列 等待任务被执行(按标准事件循环) 任务执行完成后返回
处理模型 – 执行 Task
Task Queue
我是定时任务• 开始于
• 16:45• 延迟
• 15 分钟
1 6 : 5 0
我是定时任务• 开始于
• 16:45• 延迟
• 15 分钟
1 7 : 0 0
任务任务任务
处理模型 – 执行 Taskvar doms = document.querySelector(‘div');for (var i = doms.length – 1; i >= 0; i--) { var dom = doms[i]; dom.style.color = 'red'; dom.style.lineHeight = '32px'; dom.style.width = document.body.offsetWidth; // …}
我们仍未知道那天卡死系统的元凶的位置
处理模型 – 执行 Task
Task Queue
执行 javascript• 代码入口:
• xxx
更新 DOM• Layout• Paint
执行
处理模型 – 执行 Task pause
停止事件循环的工作 更新用户界面至当前状态 等待到任务先决条件满足,这期间
不处理后续任务 当前执行的脚本暂停 界面保持响应,但无法处理输入(事件循环停了)
继续执行任务
处理模型 Event Loop -> Task Queue -> Task
多个 Task Queue 可决定部分 Task 的优先级
Task 执行过程中可能产生: spin :不影响 Event Loop 并延后执行 pause :停止 Event Loop 并延后执行
回归实践
异步的逻辑衔接事件 回调
事件模型的设计 完整性
入口 分支 1 分支 2 ……
结束 全面性
success error
beforeSend
error
dataFilter
success
complete
事件模型的设计 层次结构
全局 实例
触发顺序 逻辑前
全局 实例► 逻辑后
实例 全局►
ajaxStart
ajaxSend
beforeSend
success error
ajaxSuccess ajaxError
complete
ajaxComplete
ajaxStop
回调模型的设计统一回调
requrie('fs').readFile( 'log.txt', function(err, data) { if (err) { // ... return; }
// ... });
分支回调getCurrentAcceleration( function() { // success }, function() { // fail });
分支回调getCurrentAcceleration( function() { // success log('get accelerator success'); }, function() { // fail log('get accelerator fail'); });
逻辑重复
分支回调扩展一下:getCurrentAcceleration( onStart, onSuccess, onFail, onComplete, // … // …);
相比事件模型少了灵活性
统一回调function callback(err, data) { if (err) { // 处理错误 return; }
// 处理数据}
错误在前
错误与正确不要混淆
统一回调function censor(callback) { fs.readFile( 'log.txt', function(err, data) { if (err) { throw err; callback(err); return; } try { data = filterCensoredWords(data); callback(null, data); } catch (ex) { callback(ex); } } )}
传递错误
没有错误也要传递
捕捉一切可能的错误
AJAX 最佳实践
AJAX 最佳实践
使用模态对话框• 显眼、安全• 影响面过大,禁止其他操作• 用于进入、退出模块
AJAX 最佳实践
有效提示用户正在加载 禁用输入,以色彩注明
局部禁用• 影响面小,用户体验好• 设计、实现复杂度提高• 用于局部交互
AJAX 最佳实践第一步:把 ajax 放到 DOM 元素上
$.fn.ajax = function(options) { var dom = this; // ...}
AJAX 最佳实践第二步:添加DOM-AJAX 事件$.events.special.ajaxstart = { setup: ..., teardown: ...};$.events.special.ajaxcomplete = { setup: ..., teardown: ...};
AJAX 最佳实践第三步:代理事件$(document).on( 'ajaxstart', disableElement);$(document).on( 'ajaxcomplete', enableElement);
AJAX 最佳实践第四步:添加 CSS样式[disabled] input { background: #ccc;}[disabled] button,[disabled] div.button { color: #555;}
AJAX 最佳实践从 DOM 发起 ajax请求$('#searchForm').ajax({ url: '/search', timeout: 1200, data: { k: $('#keywords').val() }, dataType: 'json', success: fillResult});
AJAX 最佳实践
因异步时序混乱引起的问题
AJAX 最佳实践方法 1 – 闭包保留变量后重置var keyword = $('#keyword').val();$.ajax({ // ... data: { k: keyword }, success: function() { $('#keyword').val(keyword); }});
AJAX 最佳实践方法 2 – 后端返回时夹带参数$.ajax({ // ... success: function(data) { $('#keyword').val(data.keyword); $('#result').html(data.content); }});
AJAX 最佳实践 Query模式:interface IQuery<TResult> { void Fill(IEnumerable<TResult> result);}class KeywordQuery : IQuery<Entry> { public string Keyword { get; set; } public void Fill(IEnumerable<Entry> result) { // ... }}public KeywordQuery SearchByKeywords(KeywordQuery query) { var results = QueryDatabase(query.Keyword); query.Fill(results); return query;}
回调嵌套问题query('abc', null, function(data) { query('def', data, function(data) { query('xyz', data, function(data) { showResult(data); }); });});
异步常见流程 图式异步 - 回调关联
有向、无环、无权 图遍历算法
异步常见流程 标准流程:
确定异步 - 回调依赖关系 定义结点结构,绘制关系图
Node { Node[] in, Node[] out } 找到图入口
只有出边,没有入边 遍历关系图,处理回调函数
异步常见流程 并发归并
function semaphore(requests, callback) { var count = requests.length; for (var i = 0; i < count; i++) { $.get( requests[i].url, function() { count--; if (count <= 0) { callback(); } } ); }};
并行转串行
异步常见流程
function sync(requests) { var request = requests.shift(); if (request) { $.get( request.url, function() { request.callback(); sync(requests); } ); }};
回调嵌套问题流控制库
Flow-JS Step.js Async Async.js futureJS
Step( function() { query('abc', null, this); }, function(data) { query('def', data, this); }, function(data) { query('xyz', data, this); }, showResult);
回调嵌套问题 EventProxy
利用事件机制解耦复杂业务逻辑 移除被广为诟病的深度 callback嵌套问题 将串行等待变成并行等待,提升多异步场景下的
执行效率 无平台依赖,适合前后端,能用于浏览器和
Node.js https://github.com/JacksonTian/eventproxy
回调嵌套问题
回调嵌套问题 jscex
为 JavaScript语言提供了一个 monadic扩展,能够显著提高一些常见场景下的编程体验。
完全使用 JavaScript 编写,能够在任意支持ECMAScript 3 的执行引擎里使用,包括各浏览器及服务器端 JavaScript 环境。
https://github.com/JeffreyZhao/jscex
回调嵌套问题
回调嵌套问题 IcedCoffeeScript
是 CoffeeScript 的一个超集。 添加了 await 和 defer 这 2 个关键字,提供了简化异步编程的能力。
可同时用于服务器端和浏览器端。 由 CoffeeScript 编写。 http://maxtaco.github.com/coffee-script/
回调嵌套问题
回调嵌套问题 TameJS
http://tamejs.org/ StratifiedJS
http://onilabs.com/stratifiedjs Kaffeine
http://weepy.github.com/kaffeine/ streamlinejs
https://github.com/Sage/streamlinejs node-fibers
https://github.com/laverdet/node-fibers
<em>Thanks</em>