js中的非阻塞IO和回调函数

        最近自己想学习下微信小程序,学习了几个小案例之后,里面的javascript代码已经彻底把我整晕了,它不像C语言一下,定义函数、使用函数、函数返回值。javascript里面有那么多异步啦回调啦,函数简直没有个函数的样子,之前虽然也接触过JavaScript,但也仅限于在做网页交互的时候,照葫芦画瓢的用一下,现在才发现对JavaScript中好多核心的概念不了解,我最想了解的概念是:回调函数,于是开始各种百度看文章看文档,总结整理此文。

        在说回调函数之前,先简单说一下JavaScript另一特性:非阻塞IO,之前只是听说过这个词,举个例子感受一下(例子来源大佬写的一篇文章:nodejs setTimeout函数使用,在此引用并致谢):

//非阻塞IO,用setTimeout模拟文件IO等耗时操作

//定义函数abc

function abc(){

    setTimeout(function(){console.log('3')},3000);  //暂停3秒后,输出:3

    console.log('1');   //输出:1

    setTimeout(function(){console.log('2')},1000);  //暂停1秒后,输出:2

}

//调用函数abc

abc();

//如果按顺序执行,执行完一行再执行下一行

//执行的结果应该是:3,1,2运行时间约4秒

//node.js采用非阻塞IO,程序按顺序执行,但并不等待当前代码执行完毕,即非阻塞IO。

//实际执行结果是:1,2,3,运行时间约3秒

//(这种方式执行代码给我的冲击还是非常大的,竟然还可以这样???c语言老师从来没讲过啊。。。)

//这就是非阻塞IO所带来的好处,永远不会产生死锁,因为它本身没有锁机制。

//同时,非阻塞IO也会带来一些问题:

//过程式编程中,有很多情况下是本句代码要求先前的代码执行完毕,

//如要调用之前处理的数据结果、和数据库交互等。

//nodejs中可以采用回调方式解决这个问题。

        怎么通过回调来解决这个问题呢?我们把上面的例子修改一下,以便更清楚的描述我们遇到的问题:JavaScript的异步带来了高效,现在假如第一行的代码是耗时的文件读取(我们仍然用setTimeout方法模拟耗时操作),第二行的代码需要引用所读取到的文件,展示文件的内容,会是怎么的结果呢?看下面的例子:

//定义函数abc

function abc(){

    var res=""; //存放读取的文件内容

    //3秒后,文件读取完成,将内容放入到之前定义的变量res中

    setTimeout(function(){res="i am file text";},3000);

    //展示文件内容

    console.log('the file is:'+res);

}

//执行函数abc

abc();

这时的执行结果是:the file is :null

这显然不符合我们得初心啊,刚才说了,可以使用回调函数解决这个问题,那么,先来看一下回调函数的定义,JS Api 里这样解释:

        A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

        大致意思是,回调函数作为参数传递到另外一个主函数中,待主函数执行完成后,执行回调函数。

        具体到上面的例子,主函数应该是负责读取文件内容,回调函数负责处理读取到的文件内容,此处即展示文件内容。先来定义一个主函数:

//定义一个主函数main,负责读取文件内容:

function main(){

var res=""; //存放读取的文件内容

//3秒后,文件读取完成,将内容放入到之前定义的变量res中

    setTimeout(function(){res="i am file text";},3000);

}

//定义一个回调函数callback,负责展示文件内容:

function callback(res){

    console.log("the file is:"+res);

}

        下面的问题如何将主函数与回调函数联系起来呢?按照定义,将回调函数作为参数传递给主函数,所以需要改造一下主函数,给主函数添加一个参数,并在读取文件完成后,将读取的文件内容传递给回调函数(粗体+斜体标出部分):

//重新定义主函数

function main(someFunction){

    var res=""; //存放读取的文件内容

    //3秒后,文件读取完成,将内容放入到之前定义的变量res中

    setTimeout(function(){res="i am file text"; someFunction(res);},3000);

}

//执行定义的主函数和回调函数:

main(callback);

//执行的结果:

the file is: i am file text

        至此,我们已经成功的利用回调函数解决了js非阻塞特性中,特定步骤完成后再执行其他代码的需求。

        另外,在实际的应用中,回调函数往往不是提前定义好的,而是在执行主函数时定义的匿名函数处理主函数返回的结果,如上例,只定义主函数,不定义回调函数,执行主函数时再定义匿名的回调函数(粗体+斜体的部分为匿名回调函数):

//回调函数为匿名函数

main(function(res){

    console.log(“the file is :”+res);

});

        或者,通过箭头函数的形式,在执行主函数时定义回调函数(粗体+斜体的部分为匿名回调函数):

//回调函数为箭头函数

main(res=>{

    console.log(“the file is : ”+res);

});

        关于匿名函数和箭头函数,这里不再展开,请自行查看文档。以上,是我对JavaScript回调函数的理解,希望对你有帮助。

你可能感兴趣的:(js中的非阻塞IO和回调函数)