2009年6月30日星期二

写个 JavaScript 异步调用框架 (Part 5 - 链式实现)

在上一篇文章里面,我们为异步调用框架设计了一种链式调用方式,来增强异步调用队列的代码可读性,现在我们就来编写实现这部分功能的代码。

调用入口

链式调用存在Async.go方法和Async.chain方法两个入口,这两个入口本质上是一致的,只是Async.chain方法在调用时先不提供初始参数,而Async.go方法在调用时提供了初始参数并启动异步调用链。

Async.chain = function() {
  var chain = new Async.Operation({ chain: true });
  return chain;
};

Async.go = function(initialArgument) {
  return Async.chain().go(initialArgument);
}


在这里我们可以看到,链式调用本身也是一个Async.Operation,链式调用所需的go方法和next方法都是在Async.Operation上面做的扩展,并且这个扩展不会很难,这将在下一小节说明。

扩展方法

我们都知道,通过addCallback方法添加的回调函数是会被逐一执行的,至少同步函数如此,因此我们可以用Async.Operation的这一特性来维护异步调用队列,前提是我们为它加上对异步调用进行队列的支持。

对于异步调用进行队列的支持,我们稍后再来处理,首先我们利用现成的addCallback方法和yield方法扩展出go方法和next方法。

this.go = function(initialArgument) {
  return this.yield(initialArgument);
}

this.next = function(nextFunction) {
  return this.addCallback(nextFunction);
};


实际上,go方法和next方法直接调用的正是yield方法和addCallback方法。go方法的语义与yield方法一样,传递一个参数给Async.Operation实例,并且启动调用队列。同时,next方法的语义和addCallback方法,添加一个调用到队列的末端。

异步队列

如何才能让原本仅支持同步的队列变得也支持异步?这需要检测队列中的每一个调用的返回,如果返回类型为Async.Operation,我们知道是异步调用,从而使用特殊的方法等它执行完后再执行下去。

callbackResult = callback(self.result);
self.result = callbackResult;
if (callbackResult && callbackResult instanceof Async.Operation) {
  innerChain = Async.chain();
  while (callbackQueue.length > 0) {
    innerChain.next(callbackQueue.shift());
  }
  innerChain.next(function(result) {
    self.result = result;
    self.state = "completed";
    self.completed = true;
    return result;
  });
  callbackResult.addCallback(function(result) {
    self.result = result;
    innerChain.go(result);
  });
}


如果调用返回了一个Async.Operation实例,我们就利用它自身的addCallback方法帮我们执行队列中余下的调用。准确来说,是我们构造了一个新的调用链,把队列余下的调用都转移到新的调用链上,然后让当前异步调用在回调中启动这个新的调用链。

此外还有一些地方我们需要略作修改,以兼容新的异步调用队列的。例如result、state、completed的状态变更,在链式调用中是有所不同的。

小结

我们在原有的Async.Operation上略作修改,使得它支持异步调用队列,完整的代码看这里:支持链式调用的异步调用框架Async.Operation

现在我们已经拥有了一个功能强大的Async.Operation,接下来我们就要看看如何将它投入到更多常见的使用模式中去,如果你不希望错过相关讨论的话,欢迎订阅我的博客:

2009年6月15日星期一

中国程序员有美国梦吗?

Jeff最近转载了一篇名为《贺计算机成“就业最困难专业”》的文章,然后抛出了一个问题来,问大家对此看法如何,接着自然又引起了新一轮博客园首页发文热潮。对此,我站在我的角度说说我的看法。

大浪淘沙,金子难寻

1848年,美国爆发了加州淘金热潮,大量人口涌到加州进行淘金,其直接后果就是让一个叫旧金山小村庄的转眼间变成了一座大城市。在淘金热潮之初,你拿个筛子在河床里筛泥沙也能找到金子,这是何其容易的事情。但我们假想一下,如果当时一个淘金者拿着他的筛子跑到中国来,他能够淘到金子吗?我觉得是不可以的。这不是说中国的黄金矿藏量就比不上美国的一个州,而是中国没有如此高浓度而且可以进行露天开采的金矿。

1971年,旧金山湾区南端由于“淘沙子”出了名,因而被大家叫做硅谷。1998年,Google在硅谷诞生了,然后开始疯狂地吸收硅谷乃至全球的技术人才。2006至2007年,Google在中国进行大规模招聘,这时候他们遇到了跟淘金者中国之行一样的难题……

中国是第一个成功迫使Google进行笔试的国家。这句话说出来可能会让大家觉得十分搞笑,国内哪家公司招聘一线工程师不是先笔试再面试的。在人力资源供求相当的国家里,确实不需要笔试这轮筛选。有资质并且自信有资格来应聘的人,一般都能够得到充分的沟通,然后再确定这份工作是否适合。但这在人力资源明显供过于求的中国来说,就一点也不现实。

Google中国也有高层承认,以ACM/ICPC竞赛成绩来挑人确实有所缺陷,很多适合这份工作的人会被直接淘汰掉,连面对面或电话沟通的机会也没有。但假如你去看一下Google中国的校园招聘场面,看一下在各省重点大学里连开多个大教室进行笔试的情景,你不得不承认,要跟所有这些学生进行沟通的成本实在是连Google中国也支付不起。

总结一下,中国不是没有人才,也不是培养人才的方法有严重缺陷,至少这些都不是根本问题。根本问题是,在如此庞大的人口基数下,再好的企业都只能选用一套折中的人才选拔方案。你不能怪企业,说假如能生在美国你就能被Google美国所挖掘,而现在则被Google中国拒之门外。

人人都会发光的时候你该怎么办?

有时候用“是金子早晚会发光”的说法来自我安慰一下,也不是什么坏事。但当你发现人人都会发光的时候,你就知道事情有多不好了。

我们来做个数学建模,假设美国计算机人才中的top 100, top 1,000, top 10,000分别叫做A类、B类、C类。那么美国的人力资源供需关系大概是这样的:
  • 需求:100人;供应:100人。
  • 需求:1,000人;供应:1,000人。
  • 需求:10,000人;供应:10,000人。


在保持A类、B类、C类界线不变的情况下,放到中国来很可能就是这样子的:
  • 需求:500人;供应:1,000人。
  • 需求:5,000人;供应:100,000人。
  • 需求:50,000人;供应:10,000,000人。


就算中国的人才需求是美国的5倍,但依然供过于求。就算顶级人才的供求矛盾不明显,人才结构上的不合理也会给底层造成巨大的压力。

在A类里面,只有500人做不了自己应做的工作,被迫降级去做B类的工作。对于人口如此众多的一个国家来说,让500人屈就一下不是什么大问题。在B类里面,会有95,000人做不了自己应做的工作。而到了C类里面,会有9,950,000人做不了自己应做的工作,这时候问题就很严重了。

如果你搞不懂上面那堆数字说的是什么,那么我尝试换用浅白一些的语言来再说一次。如果你在美国,并且你达到了Microsoft美国招聘的要求,那么你成功加入Microsoft的概率就相当高了,因为你和Microsoft相互需要对方。

但如果你在中国,并且你达到了Microsoft中国招聘的要求,那么你成功加入Microsoft的概率并不怎么高,因为跟你一样符合这一条件的人数要远多于Microsoft的需求。如果你想要确保一个较高的应聘成功率,那么你必须远高于Microsoft的招聘要求。

没有压力的都跳槽了

假如我们把胜任某个职位的人简单分为两类:刚刚好胜任的,以及过度胜任的。那么,我们可以得到一个有趣的推论。

刚刚好胜任的人,是一定很有压力的。如果上面的分析没错的话,你之所以能够获得这个职位,一定程度上依赖于运气。好几个拥有同等实力的人跟你抢这个职位,尽管他们没能抢到,但依旧对此虎视眈眈,时刻准备着抢你饭碗。企业没理由对此坐视不理,一定会让你一直处于失业的边缘,以此作为筹码逼你好好干活,这时候你没压力才怪。

过度胜任的人,忠诚度一定会打折扣。总有更好的职位在对你招手,尽管你知道那些职位你只能凭运气获得,尽管你知道得到了你也站到了失业的边缘上,但你就不心动吗?肯定还是会心动的。

知足常乐者?估计在顶端会多一些。我和Jeff这样的,属于在国内少数算是享有American Dream的人,也就是能够通过自己的奋斗来获得应有的财富与社会地位。Jeff倾向于认为这对广大程序员普遍适用,并且鼓励大家都去追求自己的梦想。然而我得到的结论却并非如此。对于这事情,你怎么看?