co的前世今生(YY篇)
序: 有时候很羡慕大牛,他们的作品简单易用又能解决问题,不过我们往往乐于做一个使用者,而不是发明者,最近在看一些大牛的框架,学习技能的同时,也想揣摩一下他们开发历程,思考一下,是什么激发了他们点子,希望吸取经验,以后自己也写出牛逼的东西。今天我就拿co来举例,臆想一下它的前世今生。
大胆设想
背景:callback以及其导致的金字塔问题
OK,大家都讨厌使用嵌套callback,那么怎么写,才舒服呢?我想最好是可以做到:代码风格是同步的,但执行却是异步的,那么我们先假设我们已经实现了一个这样的框架,叫:wish
那么我们先来使用一下他:
var wish = require('wish');
wish(function () {
var result1 = async_task1();
console.log('result1 = ', result1);
var result2 = async_task2();
console.log('result2 = ', result2);
})
需求分析
这样还蛮爽的,我们来分析一下需求,我们的核心问题是有几个异步任务,每执行完一个异步任务的时候,就去执行下一个异步任务,直到结束,我们需要实现这样的一个流程控制工具。
OK,现在我们有两个主角,一个是异步任务,一个是任务管理者。管理者负责启动任务,并在他结束的时候,启动下一个任务。
那么现在我们有哪些道具能用呢?
技术选型
首先是异步任务,最好的抽象一个异步任务应该是Promise了,几乎任何一个异步任务都可以封装成Promise,它简直就是一个异步任务的泛型模板。
然后是任务管理者,这个选型就有很多了,可以自己封装一个对象来管理,不过因为Generator的有yield和next,正中下怀。
do it
那么有了Generator和Promise,让我们封装一个wish出来吧。 我们的主旨是,用Generator做流程控制,用Promise做异步任务封装。
function wish(generator) {
var gen = generator(); // 异步任务管理器
loop() // 每次执行一个任务
function loop(res) {
var ret = gen.next(res)
if (ret.done) {
return // 到此结束
}
var promise = (ret.value) // 把ret.value封装成promise
promise.then(loop) // promise完成后,调用loop,执行下一个任务
}
}
wish(function *(){
var result1 = yield Promise.resolve('111111');
console.log('result1=', result1);
var result2 = yield Promise.resolve('222222');
console.log('result2=', result2);
})
其实这就是co的最简模型,很简单对吧,带着这个核心思想去看co源码,就容易多了。