网站首页 > 技术教程 正文
上篇文章JavaScript基础——你真的清楚JavaScript是什么吗?,我们明白了JavaScript是一个单线程、非阻塞、异步、解释性语言,清楚了什么是单线程、进程、阻塞、调用堆栈、异步回调、任务循环等感念,没看的或者不清楚的建议点击《JavaScript基础——你真的清楚JavaScript是什么吗?再看一遍,只有理解了,才能轻松阅读理解本篇文章内容。
什么是callback?
JavaScript 是单线程工作,这意味着两段脚本不能同时运行,而是必须一个接一个地运行。我们人类是多线程工作。您可以使用多个手指打字,可以一边开车一边与人交谈。唯一一个会妨碍我们的是打喷嚏,因为当我们打喷嚏的时候,所有当前进行的活动都必须暂停。这真是非常讨厌,尤其是当您在开车并想与人交谈时。您可不想编写像打喷嚏似的代码。JavaScript由于单线程限制,防止阻塞,只能通过异步函数的调用方式,把需要延迟处理的事件放入事件循环队列。到目前为止,回调是编写和处理JavaScript程序异步逻辑的最常用方式。说了这么多,既然回调这么重要,到底什么是回调(callback)呢?
简单的定义:回调就是一个在另外一个函数执行完后要执行的函数
复杂的定义:在JavaScript中,函数是对象。因此函数可以将函数作为参数,并且可以由其他函数进行返回。执行此操作的函数称为高阶函数。任何作为参数传递的函数都称为回调函数。
为什么需要回调?
开篇已经介绍了JavaScript是单线程的,需要通回调函数处理异步相关的逻辑,理论还是过于生硬,我们还是来看段代码吧:
function first(){ console.log(1); } function second(){ console.log(2); } first(); second();
正如你所料,先执行first函数,再执行second函数,控制台将输出以下内容:
1 2
目前看来没什么问题,如果first()函数中含有某种无法立即执行的函数呢?例如,我们必须发送请求然后等待结果响应的API请求?为了模拟API请求,我们可以使用setTimeout函数模拟。我们将函数延迟500毫秒来模拟请求,我们更改后代码如下:
function first(){ // Simulate a code delay setTimeout( function(){ console.log(1); }, 500 ); } function second(){ console.log(2); } first(); second();
我们将 console.log(1) 延迟500毫秒输出,这段代码会怎么输出呢?
2 1
我们希望的顺序先执行first,再执行second,但是由于JavaScript是异步的,所有的延迟处理都要放入循环队列里,因此事与愿违,不能按照我们的希望顺序输出。如果希望这段代码按照我们的意愿输出,我们可以使用回调函数,确保某些代码执行完了,在循序执行另外一段代码。
创建回调
说了这么多,让我们创建一个简单的回调!
我们打开编辑器,先输入如下代码:
function doHomework(subject) { alert(`Starting my ${subject} homework.`); }
上面我们创建了doHomeWork的函数,我们接受一个变量,通过控制台调用,将得到下面的提示:
doHomework('math'); // Alerts: Starting my math homework.
接着,我们开始添加回调,在doHomework函数中添加一个参数callback,然后在第二个参数中回调我们定义的函数。代码如下:
function doHomework(subject, callback) { alert(`Starting my ${subject} homework.`); callback(); } doHomework('math', function() { alert('Finished my homework'); });
正如你希望的,我们在控制台里运行上述代码,将会受到两个连续的alert,Starting my math homework,然后弹出 Finished my homework。
但是回调函数并不是非得在调用函数中定义,我们可以单独定义,修改后的代码如下:
function doHomework(subject, callback) { alert(`Starting my ${subject} homework.`); callback(); } function alertFinished(){ alert('Finished my homework'); } doHomework('math', alertFinished);
此示例的输出结果和上段代码的结果一致,我们实现了在doHomework函数中调用alertFinished,实现了函数作为参数进行传递,实现了回调函数的创建。
用回调写一段真实业务场景的代码!
例如我们有一个需求,用NodeJs实现从论坛帖子列表中显示其中的一个帖子的信息及留言列表信息,代码如下:
DB/posts.json(帖子列表数据)
[ { "id": "001", "title": "Greeting", "text": "Hello World", "author": "Jane Doe" }, { "id": "002", "title": "JavaScript 101", "text": "The fundamentals of programming.", "author": "Alberta Williams" }, { "id": "003", "title": "Async Programming", "text": "Callbacks, Promises and Async/Await.", "author": "Alberta Williams" } ]
DB/comments.json(评论列表)
[ { "id": "phx732", "postId": "003", "text": "I don't get this callback stuff." }, { "id": "avj9438", "postId": "003", "text": "This is really useful info." }, { "id": "gnk368", "postId": "001", "text": "This is a test comment." } ]
Index.js
const fs = require('fs'); const path = require('path'); const postsUrl = path.join(__dirname, 'db/posts.json'); const commentsUrl = path.join(__dirname, 'db/comments.json'); //return the data from our file function loadCollection(url, callback) { fs.readFile(url, 'utf8', function(error, data) { if (error) { console.log(error); } else { return callback(JSON.parse(data)); } }); } //return an object by id function getRecord(collection, id, callback) { var collectobj=collection.find(function(element){ return element.id == id; }); callback(collectobj); return collectobj; } //return an array of comments for a post function getCommentsByPost(comments, postId) { return comments.filter(function(comment){ return comment.postId == postId; }); } loadCollection(postsUrl, function(posts){ loadCollection(commentsUrl, function(comments){ getRecord(posts, "001", function(post){ const postComments = getCommentsByPost(comments, post.id); console.log(post); console.log(postComments); }); }); });
大家请注意,我们在loadCollection函数中我们没有使用try/catch,使用的是if/else,因为catch无法从readFile方法中获取错误。上述代码还需要完善,我没有包含任何错误处理。如果在任何步骤中发生错误,程序将无法继续。
错误处理是很重要的事情,我们写代码时要严格对待,比如我们要编写一个用户登录的功能。涉及从网页表单里获取用户名和密码,查询我们的数据库,确认用户信息是否正确,验证通过后,将用户引导到用户中心页面。如果用户名密码格式不正确,用户名密码不正确,我们应该将错误信息返回给用户,并引导用户重新登录。
总结
很好!我们一起把回调的内容学完了,理解了什么是回调,异步编程是我们的代码中使用的一种方法,用于推迟事件以便以后执行。当您处理异步任务时,回调是一种解决方案,以便它们按顺序执行。
如果我们有多个任务依赖于前几个任务的结果,那我们就要使用多个嵌套回调,但是就会引发“回调地域”(过多的回调嵌套会使得代码变得难以理解与维护),还好Promise解决了“回调地狱”的问题,让我们以同步的方式编写代码,小编将会再下篇文章里进行详细介绍,敬请期待!
更多精彩内容,请微信关注”前端达人”公众号!
猜你喜欢
- 2024-09-24 深入探索C语言中的回调函数:从基础到实践
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)