node.js的同步调用是这样的:

1
2
3
4
5
6
function sync() {
return 0;
}
var x = sync();
console.log(x);

而异步调用则是这样的:

1
2
3
4
5
6
7
function async(callback) {
callback(0);
}
async(function(x){
console.log(x);
});

假如,某个函数本来是异步方式的,某天需求变了,要改成同步方式,很简单:

1
2
3
function async(callback) {
callback(sync());
}

但是,反过来则很难。因为callback原本并不存在:

1
2
3
function sync() {
//async(callback???);
}

偶然的情况下,发现了一个叫node-fibers的库,看示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Fiber = require('fibers');
function sleep(ms) {
var fiber = Fiber.current;
setTimeout(function() {
fiber.run();
}, ms);
Fiber.yield();
}
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');

Fiber居然在一个同步的方法调用内部(sleep(ms)),实现了异步调用(setTimeout),而回调正是Fiber.yield()之后的代码!

感叹作者神一般的封装之余,瞄了几眼代码,果然,是用了Native code实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* `Fiber.yield()` will halt execution of the current fiber and return control
* back to original caller of run(). If an argument is supplied to yield(),
* run() will return that value.
*
* When run() is called again, yield() will return.
*
* Note that this function is a global to allow for correct garbage
* collection. This results in no loss of functionality because it is only
* valid to yield from the currently running fiber anyway.
*
* Note also that `yield` is a reserved word in Javascript. This is normally
* not an issue, however some code linters may complain. Rest assured that it
* will run fine now and in future versions of Javascript.
*/
Fiber.yield = function(param) {
[native code]
}

node-fibers的实现方式虽然不是很’node’,但它最大的价值,是把node.js的同异步调用的转换,补上了完美的句号。