梭罗说:“我到林中去,因为我希望用力地生活,只面对生活最纯粹的真相,看看我是否领悟其真谛,免得到了临死之时,才发现自己根本没有活过……”
梭罗不是程序猿,但梭罗给程序猿也是有启示的:有些坑,只有你奋不顾身的跳进去,你才会彻悟那些代码的真相,否则到了临死之时,才发现自己根本没写过啥像样的程序……
自从摸过nodejs之后,自然是要被多重嵌套的回调函数虐的。据说COMMONJS给出了一个名叫Promise的规范,之后许多js的库都推出了promise的编程模式来方便编写异步调用的代码。对于初级的程序猿来说,这些抽象的模式,理解起来是最容易摸不着头脑的。当然笨人自然有笨人的办法,就像阿甘说的那样:stupid is as stupid does!笨人就做笨事吧——对着网络上搜索来的最简单的案例代码,一点点自己敲出来,四处console.log一下。然后,就会发现很多坑,而且这些坑可能并不是来自当前这个新东西,而是自己既有的一些基础知识不牢固。
function asyncFunction() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('Async Hello world');
}, 16);
});
}
asyncFunction().then(function (value) {
console.log(value); // => 'Async Hello world'
}).catch(function (error) {
console.log(error);
});
上面这段带码是直接从某个网站复制过来的,运行了一下,乍一看没发现什么坑。然而当我把代码改成这样,自己就给自己造了一个坑。
var Q=require('q');
var start=(new Date()).getTime();
var asyncP=function(){
return new Promise(function(resolve,reject){
setTimeout(resolve(34) ,500);
console.log('start at '+start)
})
}
function onfullfilled(v){
console.log('--'+v);
console.log('now the time span is '+((new Date()).getTime()-start));
return v+10;
}
asyncP().then(onfullfilled).then(onfullfilled).then(onfullfilled);
执行结果是这样的:
说好的500毫秒执行一次的那句“setTimeout(resolve(34) ,500);”怎么没有生效?看看打印的结果:the time span is xxxxx,没有一个是超过500毫秒的。这说明这个异步执行有大大的问题。类似的问题我之前遇到过,至今并没有找到一个合适的解释,只是自我总结为:setTimeout(func, timespan)这个方法中,func如果写作一个函数名则不能带参数,如果要需要带参数,只能写作一个匿名函数,然后在函数体内再写被引用的函数和参数,否则被引用的函数不会等到timespan之后才执行,而是立刻就执行了。下面的对比代码会看出差别来。
栗子A:
var start=(new Date()).getTime();
console.log('start at '+start)
setTimeout(test(3),1000);
function test(v){
console.log('test');
console.log('now the time span is '+((new Date()).getTime()-start));
}
执行结果是:
栗子B:
var start=(new Date()).getTime();
console.log('start at '+start)
setTimeout(test,1000);
function test(){
console.log('test');
console.log('now the time span is '+((new Date()).getTime()-start));
}
执行结果是:
栗子A和栗子B的唯一差别就是setTimeout时,一个写的是test,一个写的是test(3)。行文至此,我突然明白了,按照setTimeout的方法定义,第一个参数是function,而不是function执行的结果。所以栗子A中,setTimeout得到的第一个参数其实是undefined——因为test方法的执行结果没有返回任何东西。
原来如此,这么一个坑,差点让我以为前面找来的promise的简单实现代码是不可用的。写一写还是大大有好处的,不管你写的是代码还是文字。就算自己写给自己看,还是会让自己看见一些不一样的东西!