});
});
});
})();
这不再是 Javascript 刚刚发明的那个年代, 程序员一边用 Java 一边咒骂着花括号嵌套又嵌套何时是个头; 有经验的程序员告诫那些乳臭未干的学生军函数一定要写短, 逻辑多拆分. 有时会想着, 有什么语言会强制规定单一函数的复杂度, 以让每个函数几乎都能限制在一二十行之内呢? 结果现在这个梦想成真了, 对于 Javascript 程序员而言, 这是不得不做的纯天然方式.
比如 Javascript 中采用异步的方式来解决 IO 效率低的问题, 下面是一段用来读取 "/etc/passwd" 内容的 node 代码
fs: require('fs');
fs.readFile('/etc/passwd', function(err, data) {
if (err) {
return console.error(err);
}
console.log(data.toString());
});
fs: require('fs');
fs.open('/etc/passwd', 'r', function(err, fd) {
if (err) {
return console.error(err);
}
fs.read(fd, new Buffer(32), 0, 32, 0, function(err, bytes, buffer) {
if (err) {
fs.close(fd);
return console.error(err);
}
console.log(buffer.toString());
fs.close(fd);
});
});
所以我想, 得从坑里爬出来.
考虑到「相当一部分的异步调用貌似都是这个语句块的最后一个语句」继而有了「那为什么不在代码里面用标记让一个语句块里的语句实际上是两部分或更多部分, 第一个部分对应于当前语句块, 而后面的每个部分各是一个异步回调的函数体」这样的想法.
有了这个想法后就打起了草稿, 比如上面的前一个例子, 要是写成这样
fs: require('fs')
fs.readFile('/etc/passwd', %(err, data))
if err
return console.error(err)
console.log(data.toString())
%(err, data)
这就是一个标记, 称之为异步占位符, 它可以作为函数调用的实参出现 (不能单独写在语句中或者进行算术运算). 异步占位符表示一个匿名函数 (参数列表同异步占位符中标识符列表), 如果一个语句中的某个函数调用中有实参为异步占位符, 那么同一个语句块内, 这个函数调用之后的部分都会被移进异步占位符所表示的匿名函数的函数体中去.也就是说, 上述代码中, 该匿名函数中的代码会包括
if err
return console.error(err)
console.log(data.toString())
err
, data
这些东西.而相应地, 上面 Javascript 代码中后一个例子则可以这样编写
fs: require('fs')
fs.open('/etc/passwd', 'r', %(err0, fd))
if err0
return console.error(err0)
fs.read(fd, *Buffer(32), 0, 32, 0, %(err1, bytes, buffer))
if err1
fs.close(fd)
return console.error(err1)
console.log(buffer.toString())
fs.close(fd)
err0
与 err1
), 因为现在这段代码看起来像是一个同步代码块, 而相应地一个代码块里面不能定义两个同名的对象不是么.上面这东西, 就是 Stekinscript 的最近更新.
如果有兴致可以取下代码搞一发; 要使用 Stekinscript 编译运行上面的示例代码请使用
$ stekin -i require -i Buffer < input.stkn | node
补充说明
实现机理简述
如 Javascript 等程序在编译 / 解释的过程中都会有语句块或流的概念. 这些概念在 Stekinscript 中也存在. 只不过, 当编译到一个异步调用时, Stekinscript 将会让接下来的语句从当前语句流中分离开来, 变为一个匿名函数中的流.而异步代码之所以写起来虐, 正是因为每个异步调用将开辟一个新的语句块 (在匿名函数中), 但这些流程在同步程序设计中本是同一个语句块下先后发生的事情. 异步表达式帮助编码人员做的事情只是让这些原本在一起的兄弟姐妹各奔东西了.
使用建议
Stekinscript 异步表达式并不适用于任何异步调用或需要回调函数参数的场景, 比如jQuery.ajax
函数虽然需要回调, 但无法用异步占位符解决 (因为该回调是对象属性而非函数参数). 这种情况下请先对这函数进行包装.另外, 一旦语句块中出现了异步函数调用, 那么接下来的语句将会全部归入另一个匿名函数的函数体中, 而这个过程是不可逆的; 因此, 如果语句块中接下来有内容有一部分需要异步执行有一部分又是同步的, 不要使用异步表达式.