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原本并不存在:
偶然的情况下,发现了一个叫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的同异步调用的转换,补上了完美的句号。