教你正确使用Javascript的callback(修正)
每一个javascript的初学者至少一次会面临到这个问题:callback的函数是什么?
我们其实可以从callback这个单词本身了解一些。大概就是说在一次成功的完成任务或者任务失败之后给发起者caller的一个通知。
在本文中,我想更少地关注callback的技术层面的东西,尽可能多地用常用的自然语言来解释他们是如何工作的。也许可以帮助大家理解callback是什么,它为什么会存在。
如果你是一个javascript初学者,这篇文章绝对是你所需要的。首先,什么是一个function?
在Javascript中一个function就是一组完成一个任务的语句,这组语句可以独立于function之外存在,但是把它们放到一个function中可以使我们在其他的多个地方重复利用。
下面是一个function,将传入的偶数乘以2然后返回结果,如果传入的不是偶数,则返回原值。function doubleEven(n) { if (n % 2 === 0) { return n * 2; } return n; }
现在,只要你需要,你可以在很多的地方调用这个functiondoubleEven(10); // 输出, 20 doubleEven(5); // 输出, 5你可以将一个function作为一个参数传递给另一个function
在上面的例子,我们看到,我们可以将一个数值作为一个参数传递给一个function。同样,我们也可以把一个function作为一个参数传递给另一个function。get_info(function(){ console.log("function2 body"); })
值得注意的是,我们将整个function的定义作为参数传递给了function get_info。传入的function没有名字,称之为anonymous function。什么是callback function?
Javascript语言最重要的一个特征就是它的function可以接受另一个function作为参数的能力。
function的caller调用者可以传入一个function作为参数,然后在需要的时候触发。我们在这里举一个例子。
李雷,一个小男孩,喜欢吃pizza。一天早上他拿起手机,使用Ipizza app下单订了一个pizza。李雷选择了他最爱吃的口味,然后点击下单。
Ipizza接受了订单,告知李雷,将会在pizza准备好并开始派送时通知他。李雷,这个快乐的男孩,等了一会儿,得到了pizza做好并开始派送的通知。
如果我们分解一下这个故事,事件的顺序应该是这样的:李雷下了订单Ipizza接受订单Ipizza准备pizza,过一定的事件pizza准备好Ipizza通知李雷,pizza开始派送
通知李雷的机理跟callback function类似。让我们用编程的语言来描写这个故事
上面的顺序事件我们可以在function中用一组语句来实现。
首先,李雷下pizza订单,Ipizza接受订单。orderPizza("牛肉","芝士酱")
现在,这个orderPizza function在Ipizza服务器上的某个地方运行,并执行以下的逻辑(实际上可能会执行更复杂的逻辑,我们在这里简化):function orderPizza(type, name) { console.log("pizza已下单..."); console.log("正在准备Pizza"); setTimeout(function () { let msg = `您的 ${type} ${name} Pizza已经准备好! 总金额为 56元`; console.log(`服务器上的记录 ${msg}`); }, 3000); }
setTimeout演示了pizza准备需要一些时间。pizza准备好以后再服务器端记录一些信息。但是,出现了一个问题。
message只是记录在了服务器端,可怜的李雷不知道这个消息。我们需要告知他pizza已经准备好了。callback function的引入
我们需要引入一个callback function,让李雷知道pizza的状态。让我们更改一下orderPizza,传入一个callback function参数。注意一下,我们在pizza准备好以后,带着相关的提示信息调用callback。function orderPizza(type, name,callback) { console.log("pizza已下单..."); console.log("正在准备Pizza"); setTimeout(function () { let msg = `您的 ${type} ${name} Pizza已经准备好! 总金额为 56元`; callback(msg) }, 3000); }
现在,我们来更改一下orderPizza function的调用方式:orderPizza("牛肉","芝士酱",function(message){ console.log(message) })
现在执行的话,orderPizza的caller调用者会在pizza准备好以后收到通知的消息。这个是不是很有用呢?返回数组中的所有奇数
假设我们现在有一个数组:let numbers = [1,2,4,7,3,5,6]
为了得到数组中的奇数,我们可以使用数组array的filter(),filter根据传入的function参数来比对,将符合条件的数重新组成一个新的数组array.
下面的代码返回true,如果是奇数:function isOddNumber(number) { return number % 2; }
现在我们可以把isOddNumber作为callback传入array的filter了。const oddNumbers = numbers.filter(isOddNumber); console.log(oddNumbers);//输出为[1,7,3,5]
上例中,idOddNumber是一个callback function,当你作为参数传给一个function时,你只需要传递function的参考reference,即函数名称(不带括号对)。
使代码更简化一点,可以用anonymous function作为参数:let oddNumbers = numbers.filter(function(number) { return number % 2; }); console.log(oddNumbers); // 输出:[ 1, 7, 3, 5 ]
在ES6中,我们可以使用箭头function:let oddNumbers = numbers.filter(number => number % 2);同步Synchronous Callback function
如果我们的代码是从上到下按顺序依次执行,则它是同步的。上例的isOddNumber是同步callback function的一个例子。异步Asynchronous Callback function
Asynchronous异步指的是如果Javascript不得不等待一个操作的完成,在等待的过程中它先执行其他的代码。
假设我们现在需要开发一段代码,从服务器上下载一个图片,下载完成后处理这个图片。function download(url) { // ... } function process(picture) { // ... } download(url); process(picture);
但是,从服务器下载图片是需要时间的,根据图片的大小和网络的质量,时间长短也不一样。
下面的代码使用setTimeout模拟下载过程。function download(url) { setTimeout(() => { // 下载图片的代码在这里 console.log(`正在下载 ${url} ...`); }, 3* 1000); }
下面的代码代表处理一个图片:function process(picture) { console.log(`正在处理图片 ${picture}`); }
当我们执行如下的代码时:let url = "https://www.ilaoxu.cn/foo.jg"; download(url); process(url);
我们会得到下面的结果:正在处理图片https://www.ilaoxu.cn/foo.jg 正在下载https://www.ilaoxu.cn/foo.jg
这并不是我们想要的结果,因为在下载图片完成之前就执行了处理图片的function。正确的顺序应该是:下载一个图片,等待下载完成处理图片
为了解决这个问题,我们可以把process作为callback function传递给download(),在download function中下载完图片再执行process这个callback。function download(url, callback) { setTimeout(() => { // 下载图片的代码在这里 console.log(` 下载图片 ${url} ...`); // 图片下载完成后在调用传入的callback callback(url); }, 3000); } function process(picture) { console.log(`处理图片 ${picture}`); } let url = "https://www.ilaoxu.cn/pic.jpg"; download(url, process);
输出如下:下载图片https://www.ilaoxu.cn/pic.jpg 处理图片https://www.ilaoxu.cn/pic.jpg
现在结果如我们所愿。
在上例中,process是一个callback function传递给一个异步的function。当你在异步操作完成后使用callback来继续执行代码时,这些callback就称为异步callback。
使用异步callback,你可以在不打乱目前整体的任务的情况下,提前预定一个动作。
为了简化代码,我们可以把process写成一个anonymous function。function download(url, callback) { setTimeout(() => { // 下载图片的代码在这里 console.log(`下载图片 ${url} ...`); // 图片下载完成后在调用传入的callback callback(url); }, 3000); } let url = "https://www.ilaoxu.cn/pic.jpg"; download(url, function(picture) { console.log(`处理图片 ${picture}`); }); 嵌套callback和callback hell
我们如何按顺序下载图片和处理图片呢?典型的做法是在callback中再次调用download function。 function download(url, callback) { setTimeout(() => { //下载图片的代码在这里 console.log(`下载图片 ${url} ...`); // 图片下载完成后在调用传入的callback callback(url); }, 3000); } const url1 = "https://www.ilaoxu.cn/pic1.jpg"; const url2 = "https://www.ilaoxu.cn/pic2.jpg"; const url3 = "https://www.ilaoxu.cn/pic3.jpg"; download(url1,function(picture){ console.log(`处理图片 ${picture}`); // 下载第二张图片 download(url2,function(picture){ console.log(`处理图片 ${picture}`); // 下载第三张图片 download(url3,function(picture){ console.log(`处理图片 ${picture}`); }); }); });
输出结果:下载图片https://www.ilaoxu.cn/pic1.jpg 处理图片https://www.ilaoxu.cn/pic1.jpg 下载图片https://www.ilaoxu.cn/pic2.jpg 处理图片https://www.ilaoxu.cn/pic2.jpg 下载图片https://www.ilaoxu.cn/pic3.jpg 处理图片https://www.ilaoxu.cn/pic3.jpg
这段代码工作起来没问题,但随着项目越来越复杂,这种callback的策略就很难在随着扩展。
嵌套过多层的callback被称为pyramid of doom或者callback hell
为避免出现这种情况,可以使用Promise()或者async/await function总结:callback可以是同步的,也可以是异步的。一个Javascript的function可以接受另一个function作为参数。将function作为参数传入另一个function是一个功能强大的编程概念,可以作为当某事发生时,通知调用者的工具。也被称为callback function。你可以根据具体需要使用callback通知调用者。callback同样被用于根据不同的任务执行状态(成功,失败)来执行特定的工作逻辑。需要注意的是-嵌套过多的callback function可能不是一个好主意,可能会导致callback hell。你觉得callback还有什么优点或好的用法吗?
数字经济激活县域经济发展新动能当前,以互联网大数据人工智能为代表的新一代信息技术日新月异,数字经济已成为引领全球经济变革推动我国经济高质量发展的重要引擎。在构建新发展格局大背景下,数字化发展正成为激活区域经济的
华为新机爆料,采用三星5nm工艺的麒麟芯,这靠谱吗?受众所周知的原因影响,华为2021年上半年的高端旗舰可能要延期发布。不过,这并不影响消费者对华为P50系列的关注度。而且,网络上的相关爆料也层出不穷。消息显示,华为P50系列将在5
税惠政策助力科技创新服务冬奥揭秘科技冬奥背后的税惠元素如今,冬奥赛事正如火如荼。除了精彩的奥运赛事,这届冬奥会无处不在的科技感也让人津津乐道。从赛前预热的火炬传递到比赛现场种草无数的制服装备,再到幕后精细入微的保障服务,方方面面透露出
为什么马化腾能够辉煌十几年,而马云仅仅半年就一落千丈?那个曾经在市场上叱咤风云,拍摄功守道,让多个国家首相都重视的马云,现在逐渐消失在大家的视野中。而同家的马化腾至今已经到了知天命的时候了,却还在商业场上打拼。为什么马化腾能够辉煌十几
马云预言成笑话,京东有望取代华为成第一民企,阿里输在哪里?很反感把中国的知名的企业家和他们的企业比来比去的风气。自以为是,站在道德的高地审视众生的架势更加可恶。当枪打出头鸟成为风气,当所谓的洁癖代替商业逻辑,中国伟大的企业何以蔚然成林?阿
你手机里保留最棒的壁纸是什么?我手机里保留的最棒的壁纸,那当然是那些自己非常喜欢的那类风格,发出来跟大家分享一下壁纸随心,水墨丹青,艺术人生,或许这才是想要的。或许你喜,车水马龙的都市或许你喜,绿水青山的乡村凡
每天都抱着手机不舍得睡着是一种怎样的体验?如果24小时,除了睡觉,其它时间都是抱着手机不离手,这完全可以被诊断为手机依赖症啦。我因为工作原因,手机除了接电话打电话时间段,基本上是没有机会看手机耍朋友圈的。但如果在休息时候,
科龙空调和海信空调差距在哪里?现在海信科龙实际是一家,十几年前顾总东窗事发,海信收购了科龙容声,制定了111的双品牌计划,海信先是收购了北京雪花电器,拥有了电冰箱的生产能力,后来收购科龙容声,拥有了冰箱空调冷柜
佳能镜头24105和1635比较哪个好?除了焦叚有区别,成像质量完全满足你的需求,下靣几张都是浮动光圈28一8535一45所拍,画面还可以。虽然这两款镜头都是红圈,但不是一个档次。24105是万金油,1635则是风光利器
广汽埃安智能生态工厂二期扩建顺利竣工中国经济导报中国发展网记者皮泽红报道2月18日,埃安智能生态工厂二期产能扩建竣工并迎来首辆新车AIONLXPlus下线。值得一提的是,此次完成产能扩建并达成投产,埃安仅用时15天,
realmeGT2Pro的羊毛你薅到了吗?官方价格标错了,但订单有效日前,有网友发现,realme真我手机官网上,搭载高通骁龙8Gen1处理器的realme旗下首款高端旗舰机型GT2Pro最低配置8G128GB存储版的价格从上次调整过价格的3499