Filed under Language

Promise, Future, Dojo…

Dojo上一个普通的Ajax调用:

var xhrHandler = dojo.xhrGet({url:this.url,
  handleAs:"json-comment-optional", content:serverQuery});
xhrHandler.addCallback(dojo.hitch(this, function(data){
  this._xhrFetchHandler(data, request, fetchHandler, errorHandler);
}));
xhrHandler.addErrback(function(error){
  errorHandler(error, request);
});

addCallback时,如果deferred已经完成,则传入的function被立刻执行。所以如果上面的xhrGet很快执行完成,则加入的callback也会被执行(这个时候消息队列为空),如同代码顺序执行。如果xhrGet较长时间完成,则加入的callback会有异步运行的效果。

上面的代码感觉很奇特,有种时空的错觉。如果dojo.xhrGet(..).addCallback(…).addCallback(…)则好点。

这个东西叫Promise,在CommonJS有单独的列出来,写法可以这样requestData(“foo.bar.com”).then(…).then(),看上去直观了很多。新的Dojo.js语法里面也有这个东西,不算新,只是我对Promise这个词很感兴趣,还有Future。

当命名一个抽象概念时,名字很重要啊!

后来搜了下,原来Future在java中也有这个概念,可以参考这篇中文的文章(把callback和actor鄙视了一通),大体上讲就是把一个异步的程序转为顺序的写法:

   ExecutorService executor = ...//copy from jdk doc
   ArchiveSearcher searcher = ...
   void showSearch(final String target)
       throws InterruptedException {
     Future<String> future
       = executor.submit(new Callable<String>() {
         public String call() {
             return searcher.search(target);
         }});
     displayOtherThings(); // do other things while searching
     try {
       displayText(future.get()); // use future 这个地方会被block
     } catch (ExecutionException ex) { cleanup(); return; }

如果会被block的话,还有啥意义呢?不过代码的逻辑清楚很多。

正式的解释在wikipeida上面有:http://en.wikipedia.opreiki/Futures_and_promises Futures and promises.

这篇dojo tutorial,你发现原来dojo.then其实对应上面的future,也就是一个包含返回数据的对象(js不需要用get方法),dojo.when的用处就是使普通的数据和“将来”的数据统一使用。Wikipedia对java需要get的方式为显式的future,而dojo的为隐式的。

在Actor模式中也会用到future,具体参考Akka的这篇文档。(Akka的文档比Scala完善很多,而且有java的版本,上手很容易。上面说Future is Monad,晕啊)

Future[Object] future = actorRef.sendRequestReplyFuture[Object](msg);
Object result = future.get(); //Block until result is available, usually bad practice

actor之间可以用message来访问,上面的方式方便了非actor访问actor。如果系统明确的划分成几个Actor,则future使用的地方很少。

在对象集合中查找

最近写代码时经常遇到在一个Collection中找某个符合条件的元素。比如我要“在这些书中找到《编程之魂》”,这个简单算法在Java中得这么写:

private static Book getBookByName(List<Book> all, String bookName){
  for ( Book b : all) {
    String bName = b.getName();
    if ( bName.equals(bookName) ){
      return b;
    }
  }
}

大段的代码会打断工作的“流”:你不得不停下刚才的想法而先完成这个任务,这势必会影响效率。.NET中有个先进的玩意LINQ,用类似SQL的语法来操作collectio而不是常见的关系数据库。有了SQL语法,当然可以生成比上面复杂多的查询条件。Java开发人员只能羡慕了:在函数式编程方面,C#走在了Java前面。

Javascript中也有同样的问题,看了下jQuery,它有个方法jQuery.grep

var arr = [ 1, 9, 3, 8, 6, 1, 5, 9, 4, 7, 3, 8, 6, 9, 1 ];
arr = jQuery.grep(arr, function(n, i){
  return (n != 5 && i > 4);
});
// return 1, 9, 4, 7, 3, 8, 6, 9, 1

闭包的优势就体现出来了。而Java没有闭包,所以得整匿名函数。Google Guava扩展了JDK引以为豪的Java Collection框架(看看Guava的其他扩展,Java老矣),其中有个方法Iterables.filter(Iterable<T> unfiltered, Predicate<? super T> predicate),实现了跟上面类似的功能。匿名函数实现起来就比闭包麻烦了。

Asynchronous Javascript

本来以为Javascript是有异步机制的,后来发现其实大多数的实现都是使用setTimeout + callback,单单使用callback是不能达到异步的,比如:

	// callback style:
	function renderLotsOfData(data, callback){
		var success = false
		try{
			for(var x in data){
				renderDataitem(data[x]);
			}
			success = true;
		}catch(e){ }
		if(callback){
			callback(success);
		}
	}
	// using callback style
	renderLotsOfData(someDataObj, function(success){
		// handles success or failure
		if(!success){
			promptUserToRecover();
		}
	});
	renderOthers();

renderOthers必须等到callback函数运行完后再触发,所以其实这里是block的,只是callback的使用有“代码段”上的异步的感觉。

dojo.Defferred乍看很神秘,其实只不过是对callback函数的管理,可以有callback函数链的效果,也使复杂的情况下的编码更容易理解。

有些地方使用Node.js的方法process.nextTick来实现异步,文档里面简单的说“This is not a simple alias to setTimeout(fn, 0), it’s much more efficient.” 具体能做到多高效不得而知。而且Node.js虽然很cool,也只能在server端使用。所以感觉js中真正的异步也只有AJAX了,当然,大多数的异步要求也围绕request/response。

最好的方案大概是使用event的subscribe/publish模式了,这在Dojo和Node.js中都大量使用。要在Java中实现这种“异步的Observer模式”需要使用Executor的线程技术,不过由于缺少闭包,这时候模式的使用看上去更像为了弥补语言的缺陷。

Node.js号称任何东西都是异步的,比如I/O的操作,并以此获得Scalability。很多人对此表示怀疑,争论最后变为event和thread这两种模式的比较。Node and Scaling in the Small vs Scaling in the Large这篇指出scaling在大型系统上是综合的考虑,没有哪一种技术是“银弹”,或者说技术的选择已经无关紧要,架构或者设计才是重要的。当然这是冷静的思考,不过Node.js现在版本才0.48,大概没有人会用在大型系统中吧。我觉得event模式优点在于逻辑清晰,而thread代码复杂,难以维护。

感觉语法级别的异步和并发也只有java能做到了,也是久经考验的。Node.js的稳定性则依赖chrome V8引擎的c++代码,多少有点不靠谱,但是javascript语法灵活,这是极大优点。Ruby如果要使用异步还的靠第三方包,不过一般的web应用对这方面也不需要,php啥都没有,照样十分流行。

《Real World Haskell》读书笔记

这本书还是不错的,举了很多例子,学起来容易多了。

1). Lambda。书上讲的比较少,其实有个例子就很清楚。show me the code:

Input: map (\x -> 3*x + x/4) [1,2,3,4]

Output: [3.25,6.5,9.75,13.0]

Input: foldr1 (\x y -> 2*x + y) [1,2,3,4]

Output: 16

2. Multiple Return Value。指一个函数可以返回多个值。Haskell中使用Tuple,大小为2的tuple为pair,这比创建一个这些值的容器类更灵活,语法上更直观,比如result, ok = add(11, 22),这里ok可以用来指示程序运行是否有异常,在Go语言这种语法用来弥补C的不足。Haskell因为是静态类型的,所以这些Tuple都是有类型约束的;Go中则还可以对参数命名,这样函数的定义更加规范。

3. Pattern Matching. Twitter.