今天写代码时写了一个函数,想实现Nodejs查询pgSQL的数据查出来并把结果作为返回值,结果发现拿不到这个值,查了下资料才恍然大悟,这是Nodejs的最大特性--非阻塞! 查询数据操作作为比较消耗资源的操作,不等,直接运行后面的代码,所以会出现这样的情况,所以Nodejs中会有大量的回调函数。
作为JS的核心,回调函数和异步执行是紧密相关的,也是必须跨过去的一道个门槛。
那么究竟什么是回调函数(Callback),其实回调函数并不复杂,明白两个重点即可:
1. 函数可以作为一个参数在另一个函数中被调用。
2. JS是异步编程语言,这就是说JS代码的执行顺序并不是从上至下按部就班完成的。大多数语言都是同步编程语言,比如现在我们有3行代码,那么系统一定是一行一行按顺序向下执行的,第一行执行完了,执行第二行,紧跟着最后执行第三行,你可能会说这不是废话吗?且慢,在JS里则不尽然,比如有3行代码,并不是排在最前面的代码就是最先执行完毕的,很有可能是最后一行语句最先执行完,然后排在最前面的那行反而是最后执行完毕的,所以我们说JS是异步编程语言。
下面以node.js为例,举一个例子保证你在3步之内搞清楚究竟什么叫回调函数:
var fs = require("fs");

var c

function f(x) {

    console.log(x)

}

function writeFile() {

    fs.writeFile('input.txt', '我是通过fs.writeFile 写入文件的内容', function (err) {

        if (!err) {

            console.log("文件写入完毕!")

            c = 1

        }

    });

}

c = 0

writeFile()

f(c)
以上代码不难理解,就是设置一个全局变量c = 0,然后执行writeFile函数(也就是写入一个文件input.txt),这个函数里面有一行c = 1,函数执行完毕之后再跳出来调用f()函数,f()函数很简单,就是把打印一个变量,仅此而已。
按照 “正常” 逻辑,首先c=0,然后调用writeFile函数,该函数里面有一句c = 1,最后再调用f(c),又因为调用writeFile()是在f(c)之前,所以c=1这条语句肯定是会被执行到,那么结果应该是打印1,但是万万想不到,结果竟然是0,明明我们在writeFile函数里我们重新对c进行了赋值,为什么结果还是0呢?
因为程序运行到writeFile()这一行的时候,是一个比较耗时的IO操作,JS碰到这种操作并不会停在原地一直等待直到函数执行完毕,而是直接运行下一条代码(即f(c)),而此时 c = 1这一行代码其实并没有被执行到,所以打印出来的结果还是0 !
那你肯定会说,要解决这个问题还不容易,我们把调用f(c)也放进writeFile函数里面不就行了呗!这样就能保证c = 1之后再调用f(c)了吧?没错,就这么简单:
var fs = require("fs");

var c

function f(x) {

    console.log(x)

}

function writeFile() { 

    fs.writeFile('input.txt', '我是通过fs.writeFile 写入文件的内容', function (err) {

        if (!err) {

            console.log("文件写入完毕!")

            c = 1

            f(c)

        }

    });

}

c = 0

writeFile()

  把f(c)放进了writeFile()里面,那么c=1必然会被执行到,然后才执行f(c),不用多说,结果肯定是显示为1。但是改成这样并不完美,因为这么做就相当于将f()"焊死"在writeFile()里了,如果此处我最终想调用的函数不是f()而是别的其他函数咋整?难不成要写几个不同的writeFile(),而他们之间的区别仅仅是最后调用的那个函数不同?这样也太笨了吧,于是今天的主角:关键字callback登场了。

var fs = require("fs");

function f(x) {

    console.log(x)

}

function writeFile(callback) { //关键字callback,表示这个参数不是一个普通变量,而是一个函数

    fs.writeFile('input.txt', '我是通过fs.writeFile 写入文件的内容', function (err) {

        if (!err) {

            console.log("文件写入完毕!")

            c = 1

            callback(c) // 因为我们传进来的函数名是f(),所以此行相当于调用一次f(c)

        }

    });

}

var c = 0

writeFile(f) // 函数f作为一个参数传进writeFile函数

  

经过改造后的代码出现了两次callback关键字,第一个callback出现在writeFile的形参里,起定义的作用,表示这个参数并不是一个普通变量,而是一个函数,也就是前面所说的重点1,即所谓的“以函数为参数”。 第二个callback出现在c = 1下面,表示此处“执行”从形参传递进来的那个函数。这样一来,writeFile()函数在执行完毕之后到底调用哪个函数就变“活”了,如果我们想writeFile()函数执行完之后并不是像第二个例子那样只能调用f(),而是还有别的函数比如说x() y() z(),那么只需要写成 writeFile(x),writeFile(y)... 就行了。
我相信你已经看明白上面的代码,因为实在并不高深,那么我们现在开始用一句话攻略做一个总结:
在大多数编程语言中,函数的形参总是由外往内向函数体传递参数,但在JS里如果形参是关键字"callback"则完全相反,它表示函数体在完成某种操作后由内向外调用某个外部函数。
有时候,我们会看到一些函数的形参列表里又出现一个函数定义的情况,初时感觉一头雾水,其实只要你了解了上面的内容,看这种直接在函数调用的时候嵌入一个function的写法会很简单,其本质上仍然是回调函数,因为没有了函数名,所以也称匿名函数。
如本例如果要写成这种风格的话就是长成这样了:

var fs = require("fs");

function writeFile(callback) { 

    fs.writeFile('input.txt', '我是通过fs.writeFile 写入文件的内容', function (err) {

        if (!err) {

            console.log("文件写入完毕!")

            c = 1

            callback(c) 

        }

    });

}

var c = 0

writeFile(function (x) {

    console.log(x)

})

  writeFile()函数不变,只是在调用它的时候,直接将函数体嵌在形参列表里,其作用跟上一个例子完全一样。其实在本例中,fs.writeFile函数后面也跟了一个匿名回调函数 function (err) {},这个函数表示当文件写入完毕后,就回调它,如果在写入过程中出现了错误,则通过变量err携带出来。我相信有了前面的铺垫,您已经肯定能理解它的含义了,事实上这种写法在JS里是最常见的主流风格。

理解JS中的回调(Callback)函数的更多相关文章

  1. 深入理解js中的立即执行函数(function(){…})()

    javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解. ( f ...

  2. js中的回调函数的理解和使用方法

    js中的回调函数的理解和使用方法 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为 ...

  3. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  4. JS中的回调函数实例浅析

    本文实例讲述了JS中的回调函数.分享给大家供大家参考,具体如下: 在说回调函数之前,不妨先看一段代码,相信有点js基础的同学都能明白他的含义: ? 1 2 3 document.getElementB ...

  5. 如何理解js中的this和实际应用中需要避开哪些坑

    this是什么 this就是函数内部的关键字 看下面例子理解js中的this // 例子1 function fnOne () { console.log(this) } 'use strict' f ...

  6. JS中的高阶函数

    JS中的高阶函数 高阶函数是指以函数作为参数的函数,并且可以将函数作为结果返回的函数. 1. 高阶函数 接受一个或多个函数作为输入 输出一个函数 至少满足以上一个条件的函数 在js的内置对象中同样存在 ...

  7. 怎么理解js中的事件委托

    怎么理解js中的事件委托 时间 2015-01-15 00:59:59  SegmentFault 原文  http://segmentfault.com/blog/sunchengli/119000 ...

  8. JS中的自执行函数

    本来规划的是2013年,狠狠的将JS学习下,谁知计划赶不上变化,计划泡汤了.13年的我对JS来说可以说是属于跟风,对它的理解和认识也仅仅是皮毛而已,也是因为要完成<ArcGIS API for ...

  9. 理解Vue中的Render渲染函数

    理解Vue中的Render渲染函数 VUE一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数.比如如下我想要实现 ...

随机推荐

  1. 压缩和解压工具bandizip

    同质化的压缩软件 提及 Windows 平台的压缩软件,大家往往想起老牌的 WinRAR.开源免费的 7-Zip.国产的快压.好压.360 压缩之类,甚至还有时代的眼泪 WinZip.一直以来,压缩软 ...

  2. JavaScript 对象属性与方法

    对象的创建: 1 字面量创建 var obj = {a:1,b:2}; 2 构造函数创建 var obj = new Object(); obj.a = 1; obj.b = 2; 3 Object. ...

  3. HTML-移动端-rem px vw vh 的转换

    vw/vh rem px 三者的转换(快速入门移动端页面编写) 1:三种单位的转换 2:如何适配移动端的不同设备 前提知识: 手机端的长宽是实际设计过程中的两倍 比如手机端是 750 * 1200 那 ...

  4. VBA 学习笔记 - 变量与常量

    学习资料:https://www.yiibai.com/vba/vba_variables.html 变量和常量命名规则 必须以字母开头 不能包含空格.句点(.).感叹号(!)或字符@,&,$ ...

  5. Java面向对象简单知识总结-考试用

    类.对象.构造器 类定义了属性.方法,是抽象的,写在扩展名为java的文件中. 对象是类的实体,是具体的. 构造器:方法名与类名一致.没有返回类型,可以重载不能重写.在创建对象时调用.使用new调用实 ...

  6. lena全身像

    数字图像处理中,Lena(Lenna)是一张被广泛使用的标准图片,特别在图像压缩的算法研究中 (为什么用这幅图,是因为这图的各个频段的能量都很丰富:即有低频(光滑的皮肤),也有高频(帽子上的羽毛),很 ...

  7. python setattr()、getattr()、hasattr() 函数用法介绍

    一.函数介绍 在动态检查对象是否包含某些属性(包括方法〉相关的函数有如下几个: hasattr(object,name):检查 object 对象是否包含名为 name 的属性或方法. getattr ...

  8. POJ-1087 A Plug for UNIX (网络流)

    思路 电器数1 ~ 100,附带100种接口,注意题目:You notice that some of the devices use plugs for which there is no rece ...

  9. 使用URLConnection获取页面返回的xml数据

    public static void main(String[] args) throws Exception { String path="http://flash.weather.com ...

  10. 进程的用户ID

    进程创建时,系统会在进程上设置几个用户相关的ID 实际用户ID,实际用户组ID,系统根据当前会话登陆的用户信息设置 有效用户ID,有效用户组ID,系统根据所打开的执行文件的模式位,进行设置.set_u ...