为了解决多重嵌套回调会使代码变得难以维护的问题,javascript一直在不但的寻找改善和解决这个问题的方案,一直在不但的演进着…
异步编程的终极目标:无招(异步)胜有招(异步)
ES6之前的解决方案
嵌套回调(洋葱语法)
最简单的回调处理就是这种多重嵌套,如果嵌套太多,还要做处理异常的话,代码将变得异常复杂。
$.post('data1.json',function(data){ |
具名函数
一种减少嵌套的方法就是使用具名函数
function getData1(){ |
使用具名函数虽然减少了嵌套层级,但是直观上无法反应各个函数之间的层级关系,以致逻辑不清
Pub发布/Sub订阅
类似于具名函数,使用事件的发布订阅模式也以达到同样的效果,但依然存在着相同的问题
var event = new EventEmitter(); |
Promise
使用 promise
可让回调写起来更优雅,将回调嵌套,以链式调用的方式来写,顺序层级也很直观
$.post('data1.json') |
使用 promise
仍然也少不了回调,只是改善了回调写法
ES6的解决方案
Generator
Generator 生成器允许你通过写一个可以保存自己状态的的简单函数来定义一个迭代算法。
Generator 是一种可以停止并在之后重新进入的函数。生成器的环境(绑定的变量)会在每次执行后被保存,下次进入时可继续使用。
Generator
字面上是“生成器”的意思,在 ES6 里是迭代器生成器,用于生成一个迭代器对象。
function* gen(){ |
使用Generator可以将异步封装为同步,但无法根据异步操作后的结果自动调用 next
方法执行下一个 yield
后面的异步操作。
Generator + Promise/Thunk
结合Promise或thunk函数可以做到自动执行Generator函数,在异步操作完成之后自动调用next方法,从而实现异步流程的自动管理。
在解释thunk函数之前,先弄清楚几个相近的概念:函数柯里化、bind函数、thunk函数
函数柯里化
是指把接受多个参数的函数转变成接受单一参数的函数,并且返回一个新的函数,这个新函数能够接受原函数剩余的参数。
柯里化有3个常见作用:1. 参数复用;2. 提前返回;3. 延迟计算/运行
var curry = function(fn){ |
bind函数
用于绑定函数执行的上下文,是利用柯里化的思想,实现固定函数内部的上下文 this
,返回一个接受剩余参数的函数。
Function.prototype.bind = Function.prototype.bind || function(context){ |
thunk函数
将多参函数转换为单参函数,且只接受函数做为参数。
任何函数,只要参数有回调函数,都可以转换为thunk函数。
var Thunk = function(fn){ |
bind
与 thunk
都是柯里化思想的一种实现,第一次调用,都是返回的都是一个函数,不同的是bind返回的函数对参数没有什么要求,thunk返回的函数要求参数必须是一个函数。
Generator + Thunk
Thunk函数的特点:返回的是一个接收函数参数的函数
依次获取文件内容
var fs = require('fs'); |
Promise的特点
Promise.prototype.then(okFn, failFn) promise的then方法有两个回调,第一个是成功的回调,第二个是失败的回调,基于Promise的特点,结合Generator可以很方便控制执行流程
Generator + Promise
依次处理异步请求
function* gen(){ |
从以上可以看出,无论是 Generator+Thunk
还是 Generator+Promise
都需要封装一个执行品来做Generator执行权的转接
co函数
co的作用是自动为Generator函数添加执行器
co函数库 其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。
使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。
var co = require('co') |
ES7的终极解决方案
async + await
异步编程的最高境界,就是根本不用关心它是不是异步。
Async就像是隧道尽头的光亮,被人们奉以为是异步编程的终极解决方案。
简单来说,Async解决方案就是Generator与co的合体版
//定义一个async函数 |
sleep实现
function sleep(time){ |
并发执行
(async=>{ |
async返回的是一个Promise对象,具有Promise相关特性。
await只能用于async函数中,且只能用于普通函数中。
对比其它方法的优点:
- 内置执行器
- 更好的语义 使用
async
与await
替代*
与yield
,使得语义更清楚,async 表示后面有异步操作, await 表示需要等待后面语句返回结果 - 更广的适用性 co函数约定 yield后面必须跟thunk函数与promise函数,而async函数中,await后面可以跟Promise对象与其它任何类型
阅读参数