JS의 콜백 이해
"콜백"을 우연히 봤지만 그 의미를 몰랐던 적이 있나요? 괜찮아요. 당신은 혼자가 아닙니다. 많은 JavaScript 초보자는 콜백을 이해하는 데 어려움을 겪습니다.
콜백은 혼란스러울 수 있지만 JavaScript의 중요한 개념이므로 철저하게 이해하는 방법을 배워야 합니다. 풀백을 모르면 멀리 갈 수 없습니다.
오늘 기사에서 설명해야 할 내용은 바로 이것입니다! 콜백이 무엇인지, 왜 중요한지, 어떻게 사용하는지 배우게 됩니다.
이 기사에서는 ES6의 화살표 기능을 살펴보겠습니다. 아직 익숙하지 않다면 ES6 게시물을 먼저 확인해 보시기 바랍니다. (화살표 기능 섹션을 읽어보세요).
콜백이란 무엇인가요?
콜백은 다른 함수에 매개변수로 전달되어 나중에 실행되는 함수입니다. (개발자들은 함수가 실행되면 다른 함수가 호출되기 때문에 콜백
을 콜백이라고 부릅니다.) callbacks
称之为回调的原因)。
它们在 JavaScript 中很常见,以至于你可能不知道它们是回调函数的时候已经使用过它们。
一个可以接收回调函数的例子是addEventLisnter
:
const button = document.querySelector('button') button.addEventListener('click', function(e) { // Adds clicked class to button this.classList.add('clicked') })
没看出来这是个回调?来看看下个例子。
const button = document.querySelector('button') // Function that adds 'clicked' class to the element function clicked (e) { this.classList.add('clicked') } // Adds click function as a callback to the event listener button.addEventListener('click', clicked)
这里,我们通过 JavaScript 给一个按钮绑定了click
事件。一旦检测到了点击时间,JavaScript 会执行clicked
函数。所以,在这个例子中,当addEventListener
函数接收一个回调函数时,clicked
是一个回调。
现在知道回调是什么了么?:)
我们来看看另外一个例子。这一次,我们假设你想过滤一个数字数组来得到一个小于5
的列表。这里,你给filter
函数传递了一个回调函数。
const numbers = [3, 4, 10, 20] const lesserThanFive = numbers.filter(num => num < 5)
现在,如果你把上面的代码用具名函数改一下,那么过滤数组就会变成这样:
const numbers = [3, 4, 10, 20] const getLessThanFive = num => num < 5 // Passing getLessThanFive function into filter const lesserThanFive = numbers.filter(getLessThanFive)
在这个例子中,getLessThanFive
是个回调。Array.filter
是一个可以接收回调的函数。
现在看看?当你知道回调后会发现无处不在。
下面这个例子告诉你怎么写一个回调函数和一个可以接收回调的函数。
// Create a function that accepts another function as an argument const callbackAcceptingFunction = (fn) => { // Calls the function with any required arguments return fn(1, 2, 3) } // Callback gets arguments from the above call const callback = (arg1, arg2, arg3) => { return arg1 + arg2 + arg3 } // Passing a callback into a callback accepting function const result = callbackAcceptingFunction(callback) console.log(result) // 6
请注意,当你把回调传给另一个函数时,只是把引用传递过去了(不执行,因此没有()
)
`const result = callbackAcceptingFunction(callback)`
你只能在callbackAcceptingFunction
里调用这个回调当你这么做时,你可以给这个回调函数传递可能需要任意数量的参数:
const callbackAcceptingFunction = (fn) => { // Calls the callback with three args fn(1, 2, 3) }
这些参数通过callbackAcceptingFunction
传递到回调里,然后用它们的方式在回调里进行传递:
// Callback gets arguments from callbackAcceptingFunction const callback = (arg1, arg2, arg3) => { return arg1 + arg2 + arg3 }
这是一个回调的结构。现在,你知道了addEventListener
包含了event
参数:
// Now you know where this event object comes from! :) button.addEventListener('click', (event) => { event.preventDefault() })
唷!这是回调的基本含义!只要记住关键字:将一个函数传递到另一个函数中,你将回想起上面提到的机制。
这种传递函数的能力是一个很大的事情。它是如此之大,以至于 JavaScript 中的函数都是高阶函数。高阶函数是函数式编程范式中非常重要的东西。
但我们现在并不讨论这个话题。现在,我确信你已经知道了回调以及如何使用了。但是,你为什么需要使用回调呢?
为什么使用回调?
回调有二种不同的使用方式 - 在同步函数和在异步函数中。
同步函数中的回调
如果你的代码执行是一个从上到下,从做到右的方式,顺序地,在下一行代码执行前会等到代码执行完成,那么你的代码是同步的。
我们来看个例子,以便于更早的理解:
const addOne = (n) => n + 1 addOne(1) // 2 addOne(2) // 3 addOne(3) // 4 addOne(4) // 5
在上面的例子中,addOne(1)
先执行。当执行完成时,addOne(2)
开始执行。当addOne(2)
执行完成时,addOne(3)
开始执行。这个过程一直执行到最后一行代码被执行。
但你想让一部分代码跟其他交换简单时,这时候可以在同步的函数里使用回调。
所以,回到上面的Array.filter
例子,虽然过滤数组让其包含小于5
的数字,同样地你也可以复用Array.filter
去包含大于10
addEventLister
입니다. const numbers = [3, 4, 10, 20] const getLessThanFive = num => num < 5 const getMoreThanTen = num => num > 10 // Passing getLessThanFive function into filter const lesserThanFive = numbers.filter(getLessThanFive) // Passing getMoreThanTen function into filter const moreThanTen = numbers.filter(getMoreThanTen)
// Calls the callback after 1 second setTimeout(callback, 1000)
click
이벤트를 버튼에 바인딩합니다. 클릭 시간이 감지되면 JavaScript는 clicked
기능을 실행합니다. 따라서 이 예에서는 addEventListener
함수가 콜백 함수를 수신할 때 clicked
가 콜백입니다. 이제 콜백이 무엇인지 아시나요? :)🎜🎜또 다른 예를 살펴보겠습니다. 이번에는 5
보다 작은 숫자 목록을 얻기 위해 숫자 배열을 필터링한다고 가정해 보겠습니다. 여기서는 filter
함수에 콜백 함수를 전달합니다. 🎜const tenSecondsLater = _ = > console.log('10 seconds passed!') setTimeout(tenSecondsLater, 10000) console.log('Start!')
// What happens: // > Start! (almost immediately) // > 10 seconds passed! (after ten seconds)
getLessThanFive
는 콜백입니다. Array.filter
는 콜백을 받을 수 있는 함수입니다. 🎜🎜지금 보세요? 콜백을 알면 어디에서나 찾을 수 있습니다. 🎜🎜다음 예제에서는 콜백 함수를 작성하는 방법과 콜백을 받을 수 있는 함수를 보여줍니다. 🎜const orderPizza = flavour => { callPizzaShop(`I want a ${flavour} pizza`) waits20minsForPizzaToCome() // Nothing else can happen here bringPizzaToYou() } orderPizza('Hawaiian') // These two only starts after orderPizza is completed mopFloor() ironClothes()
()
가 없음) 🎜const addOne = (n) => n + 1 addOne(1) // 2 addOne(2) // 3 addOne(3) // 4 addOne(4) // 5 addOne(5) // 6
callbackAcceptingFunction에서만 전달할 수 있습니다. 이렇게 하면 필요할 수 있는 콜백 함수에 원하는 수만큼 인수를 전달할 수 있습니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>const orderPizza (flavor, callback) {
callPizzaShop(`I want a ${flavor} pizza`)
// Note: these three lines is pseudo code, not actual JavaScript
whenPizzaComesBack {
callback()
}
}
const layTheTable = _ => console.log(&#39;laying the table&#39;)
orderPizza(&#39;Hawaiian&#39;, layTheTable)
mopFloor()
ironClothes()</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div>🎜이 인수는 <code>callbackAcceptingFunction
을 통해 콜백에 전달된 다음 이를 사용합니다. 콜백: 🎜// Callbacks in event listeners document.addEventListener(button, highlightTheButton) document.removeEventListener(button, highlightTheButton) // Callbacks in jQuery's ajax method $.ajax('some-url', { success (data) { /* success callback */ }, error (err) { /* error callback */} }); // Callbacks in Node fs.readFile('pathToDirectory', (err, data) => { if (err) throw err console.log(data) }) // Callbacks in ExpressJS app.get('/', (req, res) => res.sendFile(index.html))
addEventListener
에 event
매개변수가 포함되어 있다는 것을 알았습니다. 🎜// Look at three layers of callback in this code! app.get('/', function (req, res) { Users.findOne({ _id:req.body.id }, function (err, user) { if (user) { user.update({/* params to update */}, function (err, document) { res.json({user: document}) }) } else { user.create(req.body, function(err, document) { res.json({user: document}) }) } }) })
🎜🎜🎜🎜콜백을 사용하는 이유는 무엇인가요? 🎜🎜🎜🎜콜백은 동기 함수와 비동기 함수의 두 가지 방법으로 사용할 수 있습니다. 🎜🎜🎜🎜동기화된 함수의 콜백🎜🎜🎜🎜코드 실행이 위에서 아래로, 오른쪽에서 왼쪽으로, 순차적으로 이루어지고 다음 코드 줄이 실행되기 전에 코드 실행이 완료될 때까지 기다리는 경우 코드는 동기식입니다. 🎜🎜이해를 돕기 위해 예를 들어보겠습니다. 🎜
const updateUser = (req, res) => { user.update({/* params to update */}, function () { if (err) throw err; return res.json(user) }) } const createUser = (req, res, err, user) => { user.create(req.body, function(err, user) { res.json(user) }) } app.get('/', function (req, res) { Users.findOne({ _id:req.body.id }, (err, user) => { if (err) throw err if (user) { updateUser(req, res) } else { createUser(req, res) } }) })
addOne(1)
이 먼저 실행됩니다. 실행이 완료되면 addOne(2)
가 실행을 시작합니다. addOne(2)
실행이 완료되면 addOne(3)
실행이 시작됩니다. 이 프로세스는 코드의 마지막 줄이 실행될 때까지 계속됩니다. 🎜🎜하지만 코드의 일부를 다른 사람과 쉽게 교환하고 싶다면 동기화된 함수에서 콜백을 사용할 수 있습니다. 🎜🎜위의 Array.filter
예시로 돌아가서 배열이 5
보다 작은 숫자를 포함하도록 필터링되었지만 배열 필터를 재사용할 수도 있습니다.
를 사용하여 10
보다 큰 숫자를 포함합니다. 🎜rrreee🎜이것이 동기화된 함수에서 콜백을 사용하는 이유입니다. 이제 계속해서 비동기 함수에서 콜백을 사용하는 이유를 살펴보겠습니다. 🎜🎜🎜🎜비동기 함수의 콜백🎜🎜🎜这里异步的意思是,如果 JavaScript 需要等待某个东西完成,在等待的过程中会执行其余的任务。
一个异步函数例子就是setTimeout
。它会一段时间后执行回调函数。
// Calls the callback after 1 second setTimeout(callback, 1000)
如果你给JavaScript 另一个任务去完成时我们看看setTimeout
是怎么工作的:
const tenSecondsLater = _ = > console.log('10 seconds passed!') setTimeout(tenSecondsLater, 10000) console.log('Start!')
在上面的代码里,JavaScript 去执行setTimeout
。这时,会等待10
秒且打印日志“10 seconds passed!”。
同时,在等到10秒去执行setTimeout
时,JavaScript 会执行console.log("Start!")
。
因此,如果你记录上面的代码,你会看到这一点。
// What happens: // > Start! (almost immediately) // > 10 seconds passed! (after ten seconds)
啊。异步操作听起来很复杂,不是么?但是我们为什么在 JavaScript 里到处使用呢?
要理解为什么异步操作很重要,想象一下 JavaScript 是你家里的一个机器人助手。这个助手很蠢。一次只能做一件事情。(这个行为称之为单线程)。
假设你告诉机器人助手帮你订点披萨。但是机器人助手如此蠢,在给披萨店打完电话后,机器人助手坐在你家门前,慢慢的等待披萨送来。在这个过程中不能做任何其他的事情。
等待的过程中,你不能让它去熨烫衣服,拖地板以及其他任何事情。你需要等20分钟,直到披萨送来,才愿意做其他的事情。
这个行为称之为阻塞。在等待一个任务执行完全之前,其他的操作被阻止了。
const orderPizza = flavour => { callPizzaShop(`I want a ${flavour} pizza`) waits20minsForPizzaToCome() // Nothing else can happen here bringPizzaToYou() } orderPizza('Hawaiian') // These two only starts after orderPizza is completed mopFloor() ironClothes()
现在,阻塞操作是非常令人失望的。
为什么?
我们把愚蠢的机器人助手放在浏览器的运行环境里。想象一下,当按钮被点击时需要改变按钮的颜色。
那这个愚蠢的机器人会怎么做呢?
它会凝视着这个按钮,在按钮被点击之前,忽略掉其他任何的命令。同时,用户不能选择其他任何东西。看看现在这样的情况?这就是异步编程在 JavaScript 为什么如此重要。
但是真正理解在异步操作过程中发生了什么,我们需要理解另外一个东西-事件循环。
事件循环
想象事件循环,可以想象 JavaScript 是一个 todo-list 的管家。这个列表包含了所有你告诉它的事情。JavaScript 会按照你给的顺序,一步步的遍历这个列表。
假设你给JavaScript 的5个命令如下:
const addOne = (n) => n + 1 addOne(1) // 2 addOne(2) // 3 addOne(3) // 4 addOne(4) // 5 addOne(5) // 6
这将会出现在 JavaScript 的todo 列表里。
命令在 JavaScript 的 todo 列表里同步显示。
除了 todo 列表,JavaScript 还保存了一个 waiting 列表,这个列表可以跟踪需要等待的东西。如果你告诉 JavaScript 需要定披萨,它会给披萨店打电话,并把"等待披萨送来"加到等到列表里。同时,它会做 todo 列表已经有的事情。
所以,想象一下有这样的代码。
const orderPizza (flavor, callback) { callPizzaShop(`I want a ${flavor} pizza`) // Note: these three lines is pseudo code, not actual JavaScript whenPizzaComesBack { callback() } } const layTheTable = _ => console.log('laying the table') orderPizza('Hawaiian', layTheTable) mopFloor() ironClothes()
JavaScript 的初始列表将会是:
定披萨,拖地和熨烫衣服!
这是,当执行到orderPizza
,JavaScript 知道需要等待披萨送来。因此,在把"等待披萨送来"加到等待列表中的同时会处理剩下的工作。
JavaScript 等待披萨到达。
当披萨送到时,按门铃会通知 JavaScript并做一个标记,当处理完其他杂事时,会去执行layTheTable
。
JavaScript 知道通过标记里的命令需要去执行layTheTable
。
然后,一旦处理完了其他的杂务,JavaScript 就会执行回调函数layTheTable
。
当其他一切都完成时, JavaScript 会将其放置。
这就是我的朋友,事件循环。你可以用事件循环中的实际关键字来替代我们的巴特勒类比来理解所有的事情。
Todo-list-> Call stack
Waiting-list-> Web apis
Mental note-> Event queue
JavaScript 事件循环
如果你有20分钟空闲时间的话,我强烈推荐你看Philip Roberts在 JSConf 上关于事件循环的演讲。它会帮助你了解事件循环里的细节。
为什么回调如此重要?
哦。我们在事件循环上转了个大圈。现在我们回头来看。
之前,我们提到如果 JavaScript 专注地盯着一个按钮并忽略其他所有的命令,这是非常糟糕的。是吧?
通过异步回调,我们可以提前给 JavaScript 指令而不需要停止整个操作。
现在,当你让 JavaScript 监听一个按钮的点击事件时,它将"监听按钮"放在等待列表里,然后继续做家务。当按钮最终获取到点击事件时,JavaScript 会激活回调,然后继续运行
下面是一些常见的回调函数,告诉 JavaScript 应该怎么做:
当事件被触发(比如:
addEventListener
)Ajax 执行之后(比如:
jQuery.ajax
)文件读写之后(比如:
fs.readFile
)
// Callbacks in event listeners document.addEventListener(button, highlightTheButton) document.removeEventListener(button, highlightTheButton) // Callbacks in jQuery's ajax method $.ajax('some-url', { success (data) { /* success callback */ }, error (err) { /* error callback */} }); // Callbacks in Node fs.readFile('pathToDirectory', (err, data) => { if (err) throw err console.log(data) }) // Callbacks in ExpressJS app.get('/', (req, res) => res.sendFile(index.html))
这就是回调!
希望,你现在已经弄清楚了回调是什么并且怎么去使用。在最开始的时候,你没必要创建很多的回调,更多的去专注于学习如何使用可用的回调函数。
现在,在结束之前,我们来看看回调的第一个问题 - 回调地狱
回调地狱
回调地狱是在多个回调嵌套出现时的一个现象。它发生在一个异步回调执行依赖上一个异步回调执行的时候。这些嵌套的回调会导致代码非常难以理解。
在我的经验里,你只会在 Node.js 里看到回调地狱。当你的 JavaScript 在前台运行时一般都不会遇到回调地狱。
这里有一个回调地狱的例子:
// Look at three layers of callback in this code! app.get('/', function (req, res) { Users.findOne({ _id:req.body.id }, function (err, user) { if (user) { user.update({/* params to update */}, function (err, document) { res.json({user: document}) }) } else { user.create(req.body, function(err, document) { res.json({user: document}) }) } }) })
现在,对你来说,解读上面的代码是一个挑战。相当的难,不是么?难怪在看到嵌套回调时,开发人员会不寒而栗。
解决回调的一个解决方案是将回调函数分解成更小的部分,以减少嵌套代码的数量
const updateUser = (req, res) => { user.update({/* params to update */}, function () { if (err) throw err; return res.json(user) }) } const createUser = (req, res, err, user) => { user.create(req.body, function(err, user) { res.json(user) }) } app.get('/', function (req, res) { Users.findOne({ _id:req.body.id }, (err, user) => { if (err) throw err if (user) { updateUser(req, res) } else { createUser(req, res) } }) })
阅读起来容易得多,不是么?
在新的JavaScript 版本里,还有一些新的解决回调地狱的方法,比如: promises
和 async/await
。但是,会在另一个话题中解析它们。
结语
今天,我们学习了什么是回调,为什么会如此重要以及如何去使用它们。同时学习到了什么是回调地狱,以及如何避免。希望,回调不会让你感到害怕。
关于回调你还有其他的问题么?如果你有的话,请在下面留言,我会尽快回复你的。
相关免费学习推荐:js视频教程
위 내용은 JS의 콜백 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.
