Promise/Generator/Co
---恢复内容开始---
这三个都是为解决回调而生的, 最近在学习Koa框架, Koa框架主要就是将异步的写法变成的同步, 解决了回调地狱的问题,也易于流程的控制, 于是找了很多篇文章学习, 终于有点感悟了~ >-<
1、Promise
看名字,Promise(承诺), 很容易联想到, 这个应该是异步操作结果的承诺, 就像数据库操作, 我们要先去find(), find到了之后就去save(), 那我们承诺find到结果后就去save, 这样就实现了异步写法变同步写法 Promise.then(DB.save(){}) 这样,我们就解决了一些些回调函数的问题了
还是比较正式一些介绍一下Promise:这是一个对象, 用于传递异步操作的信息。
它的特点
1、Promise对象代表一个异步操作, 有三种状态 Pending(进行中) Resolve(已完成) Reject(已失败) 只有异步操作的结果可以决定当前的状态
2、Promise对象的状态改变: Pending=>Resolved , Pending=>Rejected
基本用法
var promise = new Promise(function (resolve, reject) { if (find得了数据成功) { resolve(数据); } else { reject(err) } })
//通过Generator部署Ajax操作 function *main() { var result = yield request('...'); var resp = JSON.parse(result); console.log(resp.value); } function request(url) { makeAjaxCall(url, function(response) { it.next(response);//参数加上response, 作为result的值,否则返回的是undefined }) } var it = next(); it.next();
//解决回调地狱 step1(function (value1) { step2(value1, function(value2) { dothing... }) }) //如果用Promise改写的化 Q.fcall(step1) .then(step2)//step1,2都是异步操作 .then(function(){ dosth... }, function(err) { doerr... }) .done(); //用generator控制流程 function* longRunningTask() { try { var value1 = yield step1(); var value2 = yield step2(value1); } catch(e) { s.... } } //这个函数按次序自动执行所有步骤 function scheduler(task) { setTimeout(function() { var taskObj = task.next(task.value); if (!taskObj.done) { task.value = taskObj.value; scheduler(task)' } }, 0); }
/Promise实例生成后, 用then方法分别制定Resolve和Reject的回调函数 promise.then(functioin(数据) { dealWith(数据); }, function(err) { handout)(err) } )
//异步加载图片 function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); //异步加载一张图片 image.onload = function() { resolve(image);//加载成功把image传给承诺的下个函数作为参数 } image.onerror = function() { reject(new Error('Could not load image at + 'url'); } image.src = url; }) }
//异步操作的结果是另一个异步操作 var p1 = new Promise(function (resolve, reject) { doSomething(); }) var p2 = new Promise(function (resolve, reject) { resolve(p1); //p2需要等待p1的状态进行下一步操作, 等于数据库的save需要等待find的状态,这两个就是两个异步操作 })
//Promise.then //上面的可以用这个栗子改写 DB.find('someFile').then( file => save(file.AAA) ).then( AAA => doSomething(AAA), err => console.log(err) );
//Promise.all用于将多个Promise包装成一个实例 var promises = [1, 2, 3, 4, 5, 6, 7].map(function(id) { return getJSON(id + '.json'); //Promise函数 }) Promise.all(promises).then(function(posts) { do(posts); }).catch(function(reason) { }
//Generator和Promise //Generator用于管理流程 function getFoo() { return new Promise(function(resolve, reject) { resolve('foo');//返回一个Promise对象 }) } var g = function *() { try{ var foo = yield getFoo(); //迭代器到这里才会运行 console.log(foo); } catch (e) { console.log(e); } } // 简单的co function run(generator) { var it = generator(); //生成迭代器实例 function go(result) { if (result.done) { return result.value; } return result.value.then(function (value) { return go(it.next(value)); }, function(err) { return go(it.throw(error)); })' } go(it.next()); //启动迭代器遍历 } run(g)
2、Generator
可以把Generotor看成一个状态机, 封装了内部状态
执行Generator函数返回一个遍历器对象, 调用遍历器对象的next方法可以使得遍历的指针向下走
//生成一个Generator对象 function *asd() { yield 'joe'; yield 'chan'; return 'done'; } var asd = asd(); asd.next() // { value: 'joe', done: false } asd.next() // { value: 'chan', done: false } asd.next() // { value: 'done', done: true } asd.next() // { value: undefined, done: true }
//遇到yield暂停后面的操作, yield后面的表达式的值作为返回对象的value
//yield语句没有返回值(总是返回undefined),next(arg)中的arg就被当作上一个yield语句的返回值
//上面最后一点的一个应用 function *foo(x) { var y = 2* (yield x); var z = yield y; return z+y } var foo = foo(5); foo,next() //{value:5, done: false} foo.next() //{value:NaN, done: false} //因为yield的返回值是undefined foo.next(12) //{value: 24, done: false} //传入参数当作上一次yield的返回值
异步操作的同步化表达
function *loadUI() { showLoadingScreen(); yield loaUIAsyncchoromously();//异步操作 hideLoadingScreen(); //异步操作的后续操作(原来的回调函数) } var loader = loadUI(); //加载UI loader.next(); ... //卸载UI loader.next();
//通过Generator函数部署Ajax操作 function* main() { var result = yield request("http://..."); var resp = JSON.parse(result); console.log(resp.value); }//同步方式表达Ajax function request(url) { makeAjaxCall(url, function(response){ it.next(response);//response作为result的值, }); } var it = main(); it.next();//开始遍历迭代器
经典的回调hell
step1(function (value1) { step2(value1, function(value2) { dosth... }); }); //用Promise改写上面的代码 fcall(step1) .then(step2) .then(function (value2) { do... }, function (error) { doerr }) .done();
//Generator函数控制代码运行流程
function* longRunningTask() {
try {
var value1 = yield step1();
var value2 = yield step2(value1);
//do...
} catch (e) {
doerr
}
}//同步控制流程
scheduler(longRunningTask());
function scheduler(task) {
setTimeout(function() {
var taskObj = task.next(task.value); //异步操作
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}, 0);
}
参考
http://es6.ruanyifeng.com/#docs/generator