异步编程与浏览器执行模型

58
异异异异 & 异异异异异异异 Gray Zhang [email protected] 2012/03 Event

Upload: keelii

Post on 20-Dec-2014

612 views

Category:

Technology


5 download

DESCRIPTION

someone's pptx

TRANSCRIPT

Page 1: 异步编程与浏览器执行模型

异步编程&

浏览器执行模型Gray [email protected]

2012/03

Event

Page 2: 异步编程与浏览器执行模型

Summary 异步的概念 浏览器中的异步编程 浏览器执行模型 异步的逻辑衔接模型 一些最佳实践 回调维护的工具

Page 3: 异步编程与浏览器执行模型

什么是异步异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

不靠谱的百度百科

Page 4: 异步编程与浏览器执行模型

什么是异步

Page 5: 异步编程与浏览器执行模型

异步与并发

打开电脑

写函数 A 编译

休息 写函数 B

编译 休息

编译通过

git push

单一工作单元情况下可异步,但效率不会提高

Page 6: 异步编程与浏览器执行模型

异步与并发组员 A

打开电脑

写函数 A

编译

git push

组员 B

打开电脑

写函数 B

编译

git push

多个工作单元可并发,提高效率

Page 7: 异步编程与浏览器执行模型

浏览器中的异步 事件

load / mouse* / key* / click / … 定时

setTimeout / setInterval 资源

<script> / <img> / <iframe> / XHR / … 其他

requestAnimationFrame / postMessage / …

Page 8: 异步编程与浏览器执行模型

浏览器中的异步var start = new Date;var i = 0;setTimeout(function() { alert(i); }, 2000);while (new Date - start < 5000) { // sleep}i = 1;

Page 9: 异步编程与浏览器执行模型

浏览器执行模型领任

干活

领任务干活

领任务

干活

Page 10: 异步编程与浏览器执行模型

浏览器执行模型 Event Loop – 事件循环

一个浏览上下文只会有一个 Event Loop 多个浏览上下文可共享一个 Event Loop

Task Queue – 任务队列 一个 Event Loop 可有多个 Task Queue

Task Source – 任务来源 来源相同的任务肯定在同一个 Task Queue 中

Task – 任务

Page 11: 异步编程与浏览器执行模型

浏览器执行模型浏览上下文

Event Loop

Task

Task Sourcen…1

1…nTask Queue

1…n

Page 12: 异步编程与浏览器执行模型

处理模型

Event

Loop

取得最后一个Task

执行Task

移除该Task

更新用户界面

Page 13: 异步编程与浏览器执行模型

处理模型while (true) { var taskQueue = getAppropriateTaskQueue(); var task = taskQueue.peek(); runTask(task); taskQueue.dequeue(); // 细节事项 updateUI();}

Page 14: 异步编程与浏览器执行模型

处理模型 – 取得最后一个 Task

更新 DOM

• 20ms

计算公式

• 400ms

解析脚本

• 200ms

渲染图像

• 50ms

键盘输入

Page 15: 异步编程与浏览器执行模型

处理模型 – 取得最后一个 Task

Task Queue I

Task Queue II

Task Queue III

我这任务很重要

我这任务很多

我会暖床……

Page 16: 异步编程与浏览器执行模型

处理模型 – 取得最后一个 Task 可由任意算法选择一个 Task Queue ,以控制

任务的优先级

鼠标 / 键盘响应 – 3 倍优先级

事件处理 / 脚本解析 / 函数回调

Page 17: 异步编程与浏览器执行模型

处理模型 – 执行 Task

Task Queue

我是定时任务• 开始于

• 16:45• 延迟

• 15 分钟• 执行的内容

• 代码……• 计时器 ID

• 10086

1 6 : 5 0

Page 18: 异步编程与浏览器执行模型

处理模型 – 执行 Task spin – 当一个任务的先决条件不满足时:

保存当前任务的 Task Source 停止当前任务 – 事件循环执行下一个任务 异步监控任务的先决条件直到条件满足 使用保存的 Task Source ,将任务放回队列 等待任务被执行(按标准事件循环) 任务执行完成后返回

Page 19: 异步编程与浏览器执行模型

处理模型 – 执行 Task

Task Queue

我是定时任务• 开始于

• 16:45• 延迟

• 15 分钟

1 6 : 5 0

我是定时任务• 开始于

• 16:45• 延迟

• 15 分钟

1 7 : 0 0

任务任务任务

Page 20: 异步编程与浏览器执行模型

处理模型 – 执行 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; // …}

我们仍未知道那天卡死系统的元凶的位置

Page 21: 异步编程与浏览器执行模型

处理模型 – 执行 Task

Task Queue

执行 javascript• 代码入口:

• xxx

更新 DOM• Layout• Paint

执行

Page 22: 异步编程与浏览器执行模型

处理模型 – 执行 Task pause

停止事件循环的工作 更新用户界面至当前状态 等待到任务先决条件满足,这期间

不处理后续任务 当前执行的脚本暂停 界面保持响应,但无法处理输入(事件循环停了)

继续执行任务

Page 23: 异步编程与浏览器执行模型

处理模型 Event Loop -> Task Queue -> Task

多个 Task Queue 可决定部分 Task 的优先级

Task 执行过程中可能产生: spin :不影响 Event Loop 并延后执行 pause :停止 Event Loop 并延后执行

Page 24: 异步编程与浏览器执行模型

回归实践

Page 25: 异步编程与浏览器执行模型

异步的逻辑衔接事件 回调

Page 26: 异步编程与浏览器执行模型

事件模型的设计 完整性

入口 分支 1 分支 2 ……

结束 全面性

success error

beforeSend

error

dataFilter

success

complete

Page 27: 异步编程与浏览器执行模型

事件模型的设计 层次结构

全局 实例

触发顺序 逻辑前

全局 实例► 逻辑后

实例 全局►

ajaxStart

ajaxSend

beforeSend

success error

ajaxSuccess ajaxError

complete

ajaxComplete

ajaxStop

Page 28: 异步编程与浏览器执行模型

回调模型的设计统一回调

requrie('fs').readFile( 'log.txt', function(err, data) { if (err) { // ... return; }

// ... });

分支回调getCurrentAcceleration( function() { // success }, function() { // fail });

Page 29: 异步编程与浏览器执行模型

分支回调getCurrentAcceleration( function() { // success log('get accelerator success'); }, function() { // fail log('get accelerator fail'); });

逻辑重复

Page 30: 异步编程与浏览器执行模型

分支回调扩展一下:getCurrentAcceleration( onStart, onSuccess, onFail, onComplete, // … // …);

相比事件模型少了灵活性

Page 31: 异步编程与浏览器执行模型

统一回调function callback(err, data) { if (err) { // 处理错误 return; }

// 处理数据}

错误在前

错误与正确不要混淆

Page 32: 异步编程与浏览器执行模型

统一回调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); } } )}

传递错误

没有错误也要传递

捕捉一切可能的错误

Page 33: 异步编程与浏览器执行模型

AJAX 最佳实践

Page 34: 异步编程与浏览器执行模型

AJAX 最佳实践

使用模态对话框• 显眼、安全• 影响面过大,禁止其他操作• 用于进入、退出模块

Page 35: 异步编程与浏览器执行模型

AJAX 最佳实践

有效提示用户正在加载 禁用输入,以色彩注明

局部禁用• 影响面小,用户体验好• 设计、实现复杂度提高• 用于局部交互

Page 36: 异步编程与浏览器执行模型

AJAX 最佳实践第一步:把 ajax 放到 DOM 元素上

$.fn.ajax = function(options) { var dom = this; // ...}

Page 37: 异步编程与浏览器执行模型

AJAX 最佳实践第二步:添加DOM-AJAX 事件$.events.special.ajaxstart = { setup: ..., teardown: ...};$.events.special.ajaxcomplete = { setup: ..., teardown: ...};

Page 38: 异步编程与浏览器执行模型

AJAX 最佳实践第三步:代理事件$(document).on( 'ajaxstart', disableElement);$(document).on( 'ajaxcomplete', enableElement);

Page 39: 异步编程与浏览器执行模型

AJAX 最佳实践第四步:添加 CSS样式[disabled] input { background: #ccc;}[disabled] button,[disabled] div.button { color: #555;}

Page 40: 异步编程与浏览器执行模型

AJAX 最佳实践从 DOM 发起 ajax请求$('#searchForm').ajax({ url: '/search', timeout: 1200, data: { k: $('#keywords').val() }, dataType: 'json', success: fillResult});

Page 41: 异步编程与浏览器执行模型

AJAX 最佳实践

因异步时序混乱引起的问题

Page 42: 异步编程与浏览器执行模型

AJAX 最佳实践方法 1 – 闭包保留变量后重置var keyword = $('#keyword').val();$.ajax({ // ... data: { k: keyword }, success: function() { $('#keyword').val(keyword); }});

Page 43: 异步编程与浏览器执行模型

AJAX 最佳实践方法 2 – 后端返回时夹带参数$.ajax({ // ... success: function(data) { $('#keyword').val(data.keyword); $('#result').html(data.content); }});

Page 44: 异步编程与浏览器执行模型

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;}

Page 45: 异步编程与浏览器执行模型

回调嵌套问题query('abc', null, function(data) { query('def', data, function(data) { query('xyz', data, function(data) { showResult(data); }); });});

Page 46: 异步编程与浏览器执行模型

异步常见流程 图式异步 - 回调关联

有向、无环、无权 图遍历算法

Page 47: 异步编程与浏览器执行模型

异步常见流程 标准流程:

确定异步 - 回调依赖关系 定义结点结构,绘制关系图

Node { Node[] in, Node[] out } 找到图入口

只有出边,没有入边 遍历关系图,处理回调函数

Page 48: 异步编程与浏览器执行模型

异步常见流程 并发归并

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(); } } ); }};

Page 49: 异步编程与浏览器执行模型

并行转串行

异步常见流程

function sync(requests) { var request = requests.shift(); if (request) { $.get( request.url, function() { request.callback(); sync(requests); } ); }};

Page 50: 异步编程与浏览器执行模型

回调嵌套问题流控制库

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);

Page 51: 异步编程与浏览器执行模型

回调嵌套问题 EventProxy

利用事件机制解耦复杂业务逻辑 移除被广为诟病的深度 callback嵌套问题 将串行等待变成并行等待,提升多异步场景下的

执行效率 无平台依赖,适合前后端,能用于浏览器和

Node.js https://github.com/JacksonTian/eventproxy

Page 52: 异步编程与浏览器执行模型

回调嵌套问题

Page 53: 异步编程与浏览器执行模型

回调嵌套问题 jscex

为 JavaScript语言提供了一个 monadic扩展,能够显著提高一些常见场景下的编程体验。

完全使用 JavaScript 编写,能够在任意支持ECMAScript 3 的执行引擎里使用,包括各浏览器及服务器端 JavaScript 环境。

https://github.com/JeffreyZhao/jscex

Page 54: 异步编程与浏览器执行模型

回调嵌套问题

Page 55: 异步编程与浏览器执行模型

回调嵌套问题 IcedCoffeeScript

是 CoffeeScript 的一个超集。 添加了 await 和 defer 这 2 个关键字,提供了简化异步编程的能力。

可同时用于服务器端和浏览器端。 由 CoffeeScript 编写。 http://maxtaco.github.com/coffee-script/

Page 56: 异步编程与浏览器执行模型

回调嵌套问题

Page 58: 异步编程与浏览器执行模型

<em>Thanks</em>