狠狠撸

狠狠撸Share a Scribd company logo
javascript异步编程模型的演变
消费者BG——互动娱乐事业部
崔凯
01
javascript中的
异步编程场景
02
前进一小步:
Promise
05
探索异步编程
的”终极”方案
分享内容
03
其他语言的异步
模型设计:协程
04
ES6中不起眼
的特性:yield
javascript中的
异步编程场景
PART
O N E
01
发出请求
等待响应
service.do(xx,function(ret){
//receive ret
});
setTimeout(function(){
//do sth
},3000)
setInterval(function(){
// do sth
},3000)
3种异步的典型场景
延迟处理
某个操作
每隔N秒
执行操作
最容易形成多层嵌套
为什么要采用回调函数的
设计?
由于js的主线程只有1个,不断从消息队列中获取任务
所以异步操作API,只能用注册回调的方式.
否则,必然会导致阻塞当前线程
callback hell
每多一次后台操作,就多一层嵌套!
前进一小步
Promise
PART
TWO
02
Promise的state
过渡方案:Promise
Pending Fulfilled Rejected
可以看到Promise通过为异步操作返回一个可以注册回调的state,减少了嵌套层
数,但是其本质无非是把“调用时传递回调”改为了“在return结果上注册回
调”,仍然避免不了回调写法的存在
Promise版本的异步代码
Promise会捕获promise执行体里面的异常
使用者通过catch来注册异常的回调处理函数
reject之后,catch也会执行
Promise的异常处理
一般不建议使用then方法的第二个参数(失败回调)
而是直接最后使用catch方法
Promise异常处理推荐写法
IE8下,无法使用catch(保留字)作为方法名,可以用:promise[“catch”]()代替
换一种写法,当异常从Promise执行体内,通过其他异步流程会抛出时,
Promise.catch也无法捕获该异常
Promise的异常处理
即使Promise提供了诸如all,race等辅助方法,但是对于复杂的业务场景,还是
无能为力,写出来的代码可读性很差
Promise的问题
思考场景:“step1+step2,如果成功,step3+step4,否则step5+step6”
代码
标准化
then
catch
resolve
reject
...
简单场景下
嵌套确实减少了
一堆的then
代码可读性差
Promise总结
层次
扁平化
仍然
不够优雅
js回调模型已经深入人心
无缝配合异步IO
大多数前端都能轻松写出性能还不错的服务
node.js与异步io的流行
仔细看,会发现Future的思路和Promise是一样的
而写法1其实就是回调的思路
Java的AIO接口设计
运行性能 or 开发效率?
有没有一种办法,能够在性能和代码风格上达到完美平衡?
性能好 写的爽
他山之石——golang的协
程设计
P A R T
THREE
03
M: 对应系统线程
P: 调度上下文
G: goroutine(协程)
golang协程介绍
P
G
M2M1
P
G
G
block
M1
G
ip
stack
P
M1
G
ip
stack
可以看到,通过对“协程”上下文(ip,stack)的保存、恢复和调度,使用同步
API编写异步io代码是可行的
如果可以:
在js中,如果具备了以上3种能力,那么我们就应该能够写出类似的代码
性能好 写的爽
改变程序执行的入口
拿到执行结果
給入口传入参数
ES6众多特性之一——
yield
P A R T
F O U R
04
为各种数据结构,提供一个统一的、简便的访问接口
使得数据结构的成员能够按某种次序排列
供for...of消费
es6中的迭代器(Iterator)
可以看出,for...of循环,实际上就是在不断执行对象的iterator,直到done=true
生成器的初衷,是简化迭代器的定义
生成器函数执行之后,实际返回的就是一个迭代器对象
迭代器的“生成器”
yield具备“让渡执行流程”以及“返回执行结果”的能力
注意第一次next是无法传入参数的,他只是开始执行函数
每次next的时候,可以把参数传入,作为函数体内yield的返回值
神奇的next方法
next()的能力:“为后续执行步骤传递参数”
yield* 后面跟迭代器对象,在调用gen.next()的时候
遇到yield*会转为执行内部迭代器对象的next
yield*
yield:执行权“还给”调用next的代码,下一个next参数就是yield的返回
yield* 执行权“转交”另外一个迭代器对象,那他的返回值是?
yield* 会调用后面的iterator.next,一直到done:true
此时会取value值作为yield*的返回值
和for...of很像。只不过后者会忽略done:true的value
yield*的返回值
如果把inner()看成另外一个异步操作,那这种结构不就是一个“同步”写法吗?
生成器内部的未处理异常,会被外部捕获
而外部也可以通过gen.throw方法,向内部抛出异常(被内部的try捕获)
生成器的异常处理
有了异常处理能力,那么实现一个功能完善的异步编程库就成为可能
探索异步编程
“终极”方案
P A R T
F I V E
05
? 为了能够使用yield,把所有逻辑都写成生成器函数 function* ()
? 开发一个入口函数,执行生成器.next,并根据返回结果done,来判断是否继续
执行,如果继续执行,则把结果作为下一次next的参数传入
? 对于内部yield出来value,统一转换为Promise,方便处理(因为他有标准接口)
思路
demo
? 使用yield 执行异步流程
? 这个异步流程可以是一个Promise,也可以是一个Generator
? 存在的问题:
? 如果在setTimeout/setInterval中出现异常,目前并无好办法处理
可以看到,只需要简单的几十行代码,就可以让我们轻松编写异步代码了
截止到2014年时,一个人对npm社区代码贡献超过3%
express,mocha等著名框架作者
扩展了解
TJ Holowaychuk
值得一看的TJ项目
? https://github.com/mochajs/mocha
? http://koajs.com/ (express的升级版)
? https://github.com/tj/co
Thanks for
your
coming

More Related Content

闯补惫补蝉肠谤颈辫迟异步编程模型的演变

Editor's Notes

  • #5: 由于在一般情况下,通过计时器触发的任务,是被动的(系统通知我们代码可以执行),所以通过回调注册可能是唯一合理的方式,这里没有优化的必要。 (这里其实存在一个特例,就是偶尔我们会使用setTimeout来模拟java的Thread.sleep,这个时候,其实是有优化的空间的) 我们主要是考虑,正常的业务代码,请求->响应模式,如何能够避免多层嵌套回调的问题
  • #16: 基于es6之前的javascript语法,promise基本上已经是能够做到的极限了。 那么可以看看其他语言,在近期都有哪些值得我们关注的例子呢。 作为web开发人员,首当其冲的就是node.js
  • #17: 那么说到异步IO,我们自然会想到,作为主流语言的java,我们来看看目前的Java的AIO的api设计 (这里因为还没提到协程的概念,所以暂且不提java中并不特别普及、且未在标准库中支持的协程库)
  • #22: 了解过贰厂6标准的同学应该知道,别蝉6中发布了很多的新特性,
  • #23: 大家都知道,yield 只能在“生成器”(Generator)里写,在其他地方写是无效的。 那为什么叫做“生成器”呢,他“生成”的又是什么呢,所以需要先介绍一下“迭代器”(Iterator)。 PS:迭代器里,还有一个很重要的return对象属性,叫做return.这个是在for of循环异常结束的时候(没有next到done:true的时候),会执行。通常用作给迭代器闭包里的对象、资源做释放。但是由于和今天的主题关系不大,就此略去
  • #28: 生成器内部可以try,catch捕获异常 生成器对象具有throw方法,外部可以通过这个方法,向内部生成器函数抛出一个error,并被内部捕获 如果异常在内部没有捕获(不管是内部自己抛出的,还是外部通过generator.throw抛出的),就会自动抛出到外面,外面的函数通过try可以捕获到 通过generator.throw抛出的异常,被内部捕获到之后,内部会自动调一次next方法(此时生成器没有结束,还是可以继续的) 如果内部有异常没有捕获,这个函数就不会再执行下去,再调用next,将返回value:undefind,done:true的迭代器 除了异常处理,generator.return的参数将作为生成器返回的value return之后,生成器将返回done:true 相当于外部可以强制结束一个生成器函数的执行
  • #29: 前面都是铺垫,下面一起看下如何实现一个具备基本功能的异步编程库