Что такое функция колбэк
Что такое callback-функция в JavaScript?
Что такое коллбэк?
Простыми словами: коллбэк — это функция, которая должна быть выполнена после того, как другая функция завершила выполнение (отсюда и название: callback – функция обратного вызова).
Чуть сложнее: В JavaScript функции — это объекты. Поэтому функции могут принимать другие функции в качестве аргументов, а также функции могут возвращать функции в качестве результата. Функции, которые это умеют, называются функциями высшего порядка. А любая функция, которая передается как аргумент, называется callback-функцией. Чтобы лучше разобраться, давайте посмотрим на примерах, как это выглядит.
Зачем нам нужны коллбэки?
По одной простой причине: JavaScript — это событийно-ориентированный язык. Это значит, что вместо того, чтобы ждать ответа для дальнейшего выполнения программы, JavaScript продолжит выполнение, одновременно ожидая других событий. Давайте разберем простой пример:
Как вы и ожидаете, функция first выполнится первой, а функция second после нее, и в консоли будет выведен следующий результат:
Зачем я вам это показал? Чтобы вы понимали, нельзя просто вызывать функции в нужном порядке и надеяться, что они в любом случае выполнятся в том же порядке. Коллбэки же позволяют нам быть уверенными в том, что определенный код не начнет исполнение до того момента, пока другой код не завершит исполнение.
Создаем коллбэк
Хватит болтовни, теперь давайте создадим коллбэк.
Во-первых, откройте консоль разработчика в Google Chrome (Windows: Ctrl + Shift + J)(Mac: Cmd + Option + J) и введите в консоли следующую функцию:
Теперь давайте добавим в определение функции еще один параметр, это и будет наш коллбэк. Затем вызовем ее, определив функцию-callback в качестве аргумента:
Если вы введете этот код в консоли, вы получите два алерта один за другим, в первом будет сообщение о том, что выполнение домашнего задания началось (Starting my math homework.), а во втором — что вы закончили выполнять задание (Finished my homework).
Однако коллбэки не обязательно должны быть определены при вызове функции. Они могут быть определены и в другом месте кода, например, так:
Таким образом, результат выполнения этого кода такой же, как и в предыдущем примере, однако сам код немного другой. Как вы видите, мы передали функцию alertFinished как аргумент в функцию doHomework при ее вызове.
Пример из реальной жизни
На прошлой неделе я опубликовал статью «Создаем бота для Твиттера в 38 строк кода». Этот код работает благодаря API Твиттера. И когда мы делаем запрос к API, мы должны дождаться ответа до того, как начнем выполнять с этим ответом какие-то действия. Это прекрасный пример того, как в реальной жизни выглядит коллбэк. Вот как выглядит сам запрос:
T.get просто значит, что мы выполняем get запрос к API Твиттера. В запросе три параметра: ‘search/tweets’ – это адрес (роут) запроса, params – наши параметры поиска и в конце передается анонимная функция-callback.
Коллбэк здесь нужен, потому что нам нужно дождаться ответа от сервера до того, как приступим к дальнейшему выполнению кода. Мы не знаем, успешным будет наш запрос или нет, поэтому после отправки параметров поиска на search/tweets через get-запрос, мы просто ждем. Как только Твиттер ответит, выполнится наша callback-функция. Твиттер отправит нам в качестве ответа или объект err (error – ошибка), или объект response. В коллбэке мы можем через if() проверить, был ли запрос успешным или нет, и затем действовать соответственно.
Никогда не останавливайтесь: В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами — на Хекслете есть сотни курсов по разработке на разных языках и технологиях
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Понятие функции-коллбэка в JavaScript
Пусть у нас есть функция, возводящая в квадрат переданное параметром число:
Давайте с помощью функций each и square возведем каждый элемент какого-нибудь массива в квадрат:
Подобные функции, которые передаются параметрами другим функциям и выполняются где-то внутри них, называются ( англ. callback ). В нашем случае мы можем сказать, что вторым параметром функция each получает коллбэк.
Давайте внутри нашей функции each переименуем параметр с функцией, чтобы было более очевидно, что здесь будет передаваться коллбэк:
Как правило, коллбэки представляют собой анонимные функции (но это не обязательно). Давайте переделаем наш код так, чтобы во второй параметр функции each передавалась анонимная функция:
Давайте теперь реализуем функцию each :
Дан массив со строками. С помощью созданной нами функции each переверните символы каждой строки в обратном порядке.
Дан массив со строками. С помощью созданной нами функции each сделайте заглавным первый символ каждой строки.
Количество параметров в коллбэке
Давайте реализуем описанное:
Дан массив с числами. С помощью созданной нами функции each найдите произведение каждого элемента массива на его порядковый номер.
Функция filter
Пример работы такой функции (оставим в массиве только четные числа):
Дан массив с числами. С помощью созданной нами функции filter оставьте в этом массиве только положительные числа.
Дан массив со строками. С помощью созданной нами функции filter оставьте в этом массиве только строки, длина которых от 1 до 3 символов.
Функции every и some
Пример работы такой функции (проверим, что в массиве только положительные числа):
Пример работы такой функции (проверим, что в массиве есть хотя бы одно отрицательное число):
Функция alternate
Асинхронное программирование в JavaScript (Callback, Promise, RxJs )
Всем привет. На связи Омельницкий Сергей. Не так давно я вел стрим по реактивному программированию, где рассказывал про асинхронность в JavaScript. Сегодня я бы хотел законспектировать этот материал.
Но перед тем как начать основной материал нам нужно сделать вводную. Итак, давайте начнем с определений: что такое стек и очередь?
Стек — это коллекция, элементы которой получают по принципу «последний вошел, первый вышел» LIFO
Очередь — это коллекция, элементы которой получают по принципу («первый вошел, первый вышел» FIFO
JavaScript — это однопоточный язык программирования. Это значит, что он имеется только один поток выполнения и один стек, в который помещаются функции в очередь на выполнение. Следовательно в один момент времени JavaScript может выполнить только одну операцию, другие операции при этом будут ждать своей очереди в стеке, пока их не вызовут.
Стек вызовов — это структура данных, которая, упрощенно говоря, записывает сведения о месте в программе, где мы находимся. Если мы переходим в функцию, мы помещаем запись о ней в верхнюю часть стека. Когда мы из функции возвращаемся, мы вытаскиваем из стека самый верхний элемент и оказываемся там, откуда вызывали эту функцию. Это — всё, что умеет стек. А теперь крайне интересный вопрос. Как тогда работает асинхронность в JavasScript?
На самом деле помимо стека в браузерах присутствует особая очередь для работы с так называемым WebAPI. Функции из этой очереди выполнятся по порядку только после того, как стек будет полностью очищен. Только после этого они помещаются из очереди в стек на выполнение. Если в стеке в данный момент находится хотя бы один элемент, то они в стек попасть не могут. Как раз именно из-за этого вызов функций по таймаута часто бывает не точным по времени, так как функция не может попасть из очереди в стек, пока он заполнен.
Рассмотрим следующий пример и займёмся его пошаговым «выполнением». Также посмотрим, что при этом происходит в системе.
1) Пока ничего не происходит. Консоль браузера чиста, стек вызовов пуст.
2) Потом команда console.log(‘Hi’) добавляется в стек вызовов.
3) И она выполняется
4) Затем console.log(‘Hi’) удаляется из стека вызовов.
5) Теперь переходим к команде setTimeout(function cb1() <… >). Она добавляется в стек вызовов.
6) Команда setTimeout(function cb1() <… >) выполняется. Браузер создаёт таймер, являющийся частью Web API. Он будет выполнять обратный отсчёт времени.
7) Команда setTimeout(function cb1() <… >) завершила работу и удаляется из стека вызовов.
8) Команда console.log(‘Bye’) добавляется в стек вызовов.
9) Команда console.log(‘Bye’) выполняется.
10) Команда console.log(‘Bye’) удаляется из стека вызовов.
11) После того, как пройдут, как минимум, 5000 мс., таймер завершает работу и помещает коллбэк cb1 в очередь коллбэков.
12) Цикл событий берёт c функцию cb1 из очереди коллбэков и помещает её в стек вызовов.
13) Функция cb1 выполняется и добавляет console.log(‘cb1’) в стек вызовов.
14) Команда console.log(‘cb1’) выполняется.
15) Команда console.log(‘cb1’) удаляется из стека вызовов.
16) Функция cb1 удаляется из стека вызовов.
Взглянем на пример в динамике:
Ну вот мы и рассмотрели как в JavaScript реализована асинхронность. Теперь давайте кратко поговорим об эволюции асинхронного кода.
Эволюция асинхронного кода.
Асинхронное программирование, каким мы его знаем в JavaScript, может быть реализовано только функциями. Они могут быть переданы как любая другая переменная другим функциям. Так родились коллбэки. И это прикольно, весело и задорно, пока не превращается в грусть, тоску и печаль. Почему? Да все просто:
С появлением Promise обстановка стала чуть лучше.
Но у промиса есть свои ограничения. К примеру промис, без танцев с бубном, нельзя отменить, а что самое главное — работает с одним значением.
Ну вот мы и плавно подошли к реактивному программированию. Устали? Ну благо дело можно можно пойти заварить чаек, обмозговать и вернуться читать далее. А я продолжу.
Реактивное программирование — парадигма программирования, ориентированная на потоки данных и распространение изменений. Давайте более детально разберем что такое поток данных.
Представим, что у нас есть поле ввода. Мы создаем массив, и на каждый keyup события input мы будем сохранять событие в нашем массиве. При этом хотелось бы отметить, что наш массив отсортирован по времени т.е. индекс более поздних событий больше, чем индекс более ранних. Такой массив представляет собой упрощенную модель потока данных, но это еще не поток. Для того чтоб этот массив можно было смело назвать потоком он должен уметь каким-то образом сообщать подписчикам, что в него поступили новые данные. Таким образом мы подошли к определению потока.
Поток данных
Поток — это массив данных, отсортированных по времени, который может сообщать о том, что данные изменились. А теперь представьте как удобно становится писать код, в котором на одно действие потребуется вызывать несколько событий в разных участках кода. Мы просто делаем подписку на поток и он нам сам сообщит когда произойдут изменения. И это умеет делать библиотека RxJs.
RxJS — это библиотека для работы с асинхронными и основанными на событиях программами с использованием наблюдаемых последовательностей. Библиотека предоставляет основной тип Observable, несколько вспомогательных типов (Observer, Schedulers, Subjects) и операторы работы с событиями как с коллекциями (map, filter, reduce, every и подобные из JavaScript Array).
Давайте разберемся с основными понятиями этой библиотеки.
Observable, Observer, Producer
Observable — первый базовый тип, который мы рассмотрим. Этот класс содержит в себе основную часть реализации RxJs. Он связан с наблюдаемым потоком, на который можно как подписаться с помощью метода subscribe.
В Observable реализуется вспомогательный механизм для создания обновлений, так называемый Observer. Источником значений для Observer называется Producer. Это может быть массив, итератор, web socket, какое-то событие и т.п. Так что можно сказать, что observable является проводником между Producer и Observer.
Observable обрабатывает три вида событий Observer:
В начале мы обработаем значения 1, 2, 3, а спустя 1 сек. мы получим 4 и завершим наш поток.
И тут я понял, что рассказывать было интересней чем писать об этом. 😀
Subscription
Когда мы делаем подписку на поток, мы создаем новый класс subscription, который дает нам возможность отменить подписку с помощью метода unsubscribe. Так же мы можем сгруппировать подписки с помощью метода add. Ну и логично, что мы можем разгруппировать потоки с помощью remove. Методы add и remove на вход принимают другую подписку. Хотелось бы отметить, что когда мы делаем отписку, то мы отписываемся от всех дочерних подписок как будто бы и у них вызывали метод unsubscribe. Идем дальше.
Виды потоков
HOT | COLD |
---|---|
Producer создается снаружи observable | Producer создается внутри observable |
Данные передаются в момент создания observable | Данные сообщаются в момент подписки |
Нужна дополнительная логика для отписки | Поток завершается самостоятельно |
Использует связь один-к-многим | Использует связь вида один-к-одному |
Все подписки имеют единое значение | Подписки независимы |
Данные можно потерять, если нет подписки | Переиздает все значения потока для новой подписки |
Если приводить аналогию, то я бы представил горячий поток как фильм в кинотеатре. В какой момент времени ты пришел, с того момента и начал просмотр. Холодный поток я бы сравнил со звонком в тех. поддержку. Любой позвонивший слушает запись автоответчика от начала до конца, но ты можешь бросить трубку с помощью unsubscribe.
Хотелось бы отметить, что существуют еще так называемые warm потоки ( такое определение я встречал крайне редко и только на зарубежных сообществах ) — это поток, который трансформируется из холодного потока в горячий. Возникает вопрос — где использовать)) Приведу пример из практики.
Я работаю с ангуляром. Он активно использует rxjs. Для получения данных на сервер я ожидаю холодный поток и этот поток использую в шаблоне с помощью asyncPipe. Если я использую этот пайп несколько раз, то, возвращаясь к определению холодного потока, каждый pipe будет запрашивать данные с сервера, что мягко говоря странно. А если я преобразую холодный поток в теплый, то запрос произойдет единожды.
Вообще понимание вида потоков достаточно сложна для начинающих, но важна.
Operators
Расширить возможность работы с потоками нам предоставляют операторы. Они помогают контролировать события, протекающие в Observable. Мы рассмотрим парочку наиболее популярных, а подробнее с операторами можно ознакомиться по ссылкам в полезной информации.
Operators — of
Начнем со вспомогательного оператора of. Он создает Observable на основе простого значения.
Operators — filter
Оператор фильтрации filter, как можно понять по названию, фильтрует сигнал потока. Если оператор возвращает истину, то пропускает далее.
Operators — take
take — Принимает значение кол-ва эмитов, после которого завершает поток.
Operators — debounceTime
debounceTime — отбрасывает испускаемые значения, которые попадают в указанный промежуток времени между выходными данными — по прошествии временного интервала эмитит последнее значение.
Operators — takeWhile
Эмитит значения, пока takeWhile не вернет false, после чего отпишется от потока.
Operators — combineLatest
Комбинированный оператор combineLatest чем-то похож на promise.all. Он объединяет несколько потоков в один. После того как каждый поток сделает хотя бы один эмит, мы получаем последние значения от каждого в виде массива. Далее, после любого эмита из объединённых потоков он будет отдавать новые значения.
Operators — zip
Zip — ждет значение из каждого потока и формирует массив на основе этих значений. Если значение не придет из какого-либо потока, то группа не будет сформирована.
Operators — forkJoin
forkJoin также объединяет потоки, но он емитнит значение только когда все потоки будут завершены (complete).
Operators — map
Оператор трансформации map преобразует значение эмита в новое.
Operators – share, tap
Утилитный оператор share способен из холодного потока сделать горячим.
С операторами закончили. Перейдем к Subject.
И тут я пошел чаек пить. Утомили меня эти примеры 😀
Семейство subject-ов
Семейство subject-ов являются ярким примером горячих потоков. Эти классы являются неким гибридом, которые выступают одновременно в роли observable и observer. Так как subject является горячим потоком, то от него необходимо отписываться. Если говорить по основным методам, то это:
Выделяют 4 5 типов subject-ов.
На стриме говорил 4, а оказалось они еще один добавили. Как говорится век живи век учись.
Простой Subject new Subject() – самый простой вид subject-ов. Создается без параметров. Передает значения пришедшие только после подписки.
BehaviorSubject new BehaviorSubject( defaultData ) – на мой взгляд самый распространённый вид subject-ов. На вход принимает значение по умолчанию. Всегда сохраняет данные последнего эмита, которые передает при подписке. Данный класс имеет так же полезный метод value, который возвращает текущее значение потока.
ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) — На вход опционально может принять первым аргументом размер буфера значений, которые он будет в себе хранить, а вторым время в течении которого нам нужны изменения.
AsyncSubject new AsyncSubject() — при подписке ничего не происходит, и значение будет возвращено только при complete. Будет возвращено только последнее значение потока.
Фуф. Ну вот мы и рассмотрели все, что я хотел сегодня рассказать. Надеюсь данная информация была полезной. Самостоятельно ознакомиться со списком литературы можно во вкладке полезная информация.
Делегаты и колбэки в Swift простым языком. Что же такое этот delegate, и как работает callback
В Swift при изучении UI (User Interface) каждый рано или поздно приходит к необходимости использования делегата. Все гайды о них пишут, и вроде бы делаешь, как там написано, и вроде бы работает, но почему и как это работает, не у каждого в голове укладывается до конца. Лично у меня даже какое-то время складывалось ощущение, что delegate – это какое-то волшебное слово, и что оно прям встроено в язык программирования (вот, насколько запутаны были мои мысли от этих гайдов). Давайте попытаемся объяснить простым языком, что же это такое. А разобравшись с делегатом, уже гораздо легче будет понять, что такое колбэк (callback), и как работает он.
Официант и повар
Итак, перед тем как перейти к коду давайте представим себе некоего официанта и какого-нибудь повара. Официант получил заказ от клиента за столиком, но сам он готовить не умеет, и ему нужно попросить об этом повара. Он может пойти на кухню и сказать повару: «Приготовь курицу». У повара есть соответствующие инструменты (сковорода, масло, огонь…) и навык приготовления. Повар готовит и отдает блюдо официанту. Официант берет то, что сделано поваром и несет к клиенту.
А теперь представим себе ситуацию, что официант не может прибежать на кухню и сказать повару напрямую, какое блюдо у него заказали. Не пускают его на кухню (допустим, такие правила) или кухня находится на другом этаже (устанешь бегать). И единственный способ общения – это окошко мини-лифта. Официант кладет туда записку, нажимает кнопку, лифт уехал на кухню. Приезжает обратно с готовым блюдом. Запомнили? Теперь зафиксируем ситуацию в голове, попробуем воссоздать через код и понять, как это связано с нашей темой.
Перенесем в код
Создаем классы официанта и повара. Для простоты сделаем это в плейграунде:
Теперь создаем их экземпляры (нанимаем на работу), и просим официанта получить заказ (курицу):
Как теперь официанту передать повару, что ему приготовить?
Конечно, можно было бы написать ниже вот так, если бы свойства и методы официанта и повара не были private. Но так как у них уровень доступа private, то это не сработает:
И к тому же мы использовали некий сторонний код «снаружи» классов официанта и повара, которому необходимо иметь доступ к private свойствам и методам этих классов. А как официанту передать «изнутри» себя, используя встроенные свойства и методы своего класса? Тот же вопрос со стороны повара: «Как ему приготовить то, что известно только официанту, используя свойства и методы своего класса?»
Тут на помощь приходит «лифт». В этот лифт официант кладет записку с заказом. А повар берет записку из лифта и ставит в лифт готовое блюдо для передачи повару. Такой «лифт» реализуется через протокол «Взаимообмен через лифт»:
В данном случае мы говорим, что у лифта есть «интерфейс», который должен быть понятен и официанту, и повару. То есть правила взаимообмена через этот лифт. Правила взаимообмена должны знать и официант, и повар. Они простые: официант кладет записку, а повар готовит по ней.
Теперь давайте поправим классы, чтобы официант мог общаться с поваром через этот лифт. Научим их работать по этому правилу(протоколу).
Подпишем класс повара под протокол лифта. Грубо говоря, мы научим всех наших поваров соблюдать правила, описанные в этом протоколе «Обмен через лифт». В таком случае Xcode заставит нас дописать в класс повара метод из протокола. Этот метод в данном примере должен будет вернуть Bool значение.
В этом методе мы вызовем ранее созданный метод cookFood, который повар умеет выполнять.
Далее официанту добавим свойство «получатель заказа через лифт». Официант знает, что этот получатель знает правила и приготовит то, что в записке.
В данном примере мы использовали расширение, которое не позволяет делать переменные с хранимыми свойствами. Поэтому мы вынуждены дописать в фигурных скобках return cook.
Но можно сделать по-другому: просто добавить без расширения прямо внутрь класса официанта это опциональное свойство, а затем извне этому свойству присвоить экземпляр нашего повара. Главное не забыть подписать класс повара под протокол.
Давайте так и сделаем, чтобы не усложнять наш простой пример экстеншенами. Удалим эти экстешнены и просто добавим строчки в ранее созданные классы, и подпишем класс повара под протокол.
Вот теперь как будут выглядеть наши классы:
В классе официанта убрали пока private у свойства order (нужно сейчас для наглядности).
Далее все по той же схеме:
Теперь скажем официанту, что его «получатель заказа через лифт» – это наш повар.
Как уже говорилось ранее, официант знает, что этот получатель знает правила и приготовит то, что в записке.
Теперь официант может нашего «получателя заказа через лифт» попросить приготовить заказ:
Запускаем код, получаем результат!
Как Дубровский «имел связь с Машей через дупло», так и наш официант теперь имеет возможность отправлять заказы повару через протокол и известный официанту метод данного протокола.
Можно теперь доделать у класса «Официант» метод передачи заказа, чтобы официант мог делать это «изнутри себя», то есть используя свои собственные свойства и методы, а не указания со стороны.
Вот и все дела! В данном случае мы добавляли официанту опциональное свойство receiverOfOrderViaElevator, подписанное под протокол. Это свойство и есть делегат. Можете заменить название этого свойства на delegate, если хотите. По сути ничего не изменится, просто это более универсальное слово, вот и все.
Теперь вы понимаете принцип работы делегата? «Окей, – скажете вы. – А как это использовать в UI?»
Как использовать delegate при создании контроллеров в UI?
В UI с необходимостью использования делегата чаще всего встречаются в случае, когда необходимо от одного «дочернего» контроллера передать информацию «родительскому». Например, нужно передать от ячейки информацию создавшему ее table view или collection view. От table view или collection view передать информацию ячейке легко: это можно сделать при ее инициализации. А вот обратно передать информацию сама ячейка напрямую не может. Вот тут и спасает шаблон (паттерн) под названием «Делегат» («Delegate»).
Кстати, слово Delegable в русской раскладке будет как «Вудупфиду». Так вот, значит, о чем пела Мерлин Монро!
Давайте представим, что повар – это хозяин кафе. И он нанимает нашего официанта. То есть создает экземпляр класса Waiter. Добавим ему умение(метод) hireWaiter. Получим вот такой класс (кстати, пусть на этот раз это будет шеф-повар):
Теперь шеф-повар открывает кафе и нанимает официанта (создаем экземпляр шеф-повара и вызываем у него метод hireWaiter):
Далее все по старинке. Нужно обучить официанта, что его «получатель заказа через лифт» – это наш шеф-повар. И тогда он сможет передать ему заказ.
Если запустить код, то снова все сработает.
Отлично. А теперь представим, что появился новый шеф-повар, который рассказывает официанту ещё при найме на работу, что его «получателем заказа через лифт» будет сам шеф-повар.
Здесь мы просто наследовали класс SmartChief от класса Chief и переписали метод найма официантов.
Получается, что теперь нет необходимости как-то отдельно указывать официанту (обучать), кто его получатель заказа через лифт. Он с самого начала работы уже об этом знает!
Тоже самое и в контроллерах:
Естественно, это можно делать не только с «порождающими» контроллерами, а вообще с любыми объектами, которые мы хотим научить общаться друг с другом.
На этом про делегаты всё. Надеюсь, было полезно! Теперь разберемся в колбэках.
Колбэки (колбеки, callback). Это тот же делегат? Ну, почти
Итак, мы выяснили, что делегаты позволяют одним объектам «общаться» с другими. Иначе говоря, позволяют одним объектам добиваться своей цели с помощью свойств и методов других объектов. Для чего же нужны колбэки? И почему они в одной статье с делегатами?
Колбэк (callback) – это тоже шаблон программирования, как и делегат. С его помощью также одни объекты могут добиваться своих целей с помощью свойств и методов других объектов. Только делать они это будут сами.
Ленивый шеф-повар, талантливый официант
Представьте себе, что в другом ресторане шеф-повар очень ленивый (или усталый, или заболел). Официант принял заказ, звонит повару, а тот ему говорит: «Давай-ка сам приготовь! Возьми мою сковородку, поставь на плиту и приготовь по рецепту!» Наш официант оказался не из робкого десятка, берет инструкцию, инструменты повара и все делает сам. Это и есть подход через колбэк.
Давайте посмотрим это на практике.
Создадим класс талантливого официанта. Добавим опциональное свойство функционального типа. Под этим названием кроется функция, которая принимает на вход аргумент с типом String и возвращает результат с типом Bool. Прямо как метод cookFood у нашего повара! Это что-то вроде способности сделать что-то по инструкции.
Далее создаем класс ленивого шеф-повара. В этот раз при найме на работу талантливого официанта, повар не назначает себя делегатом, а обучает его самому готовить. Он передает в именованное функциональное свойство официанта свое умение готовить, которое включает в себя и доступ к сковородке с плитой. То есть он присваивает замыкание его свойству, и в этом замыкании передает свой собственный метод:
В результате после появления такого шеф-повара, найме на работу официанта, официант готовит блюдо самостоятельно, как только примет заказ:
Таким образом, как и в случае с делегатом, официант «добился» своей цели и приготовил заказ.
Получается, что способ взаимосвязи через колбэк выглядит даже несколько короче, чем способ через делегат. По крайней мере, не нужно заморачиваться с протоколами, строить правила (лифты).
Однако у этого способа есть один минус – создание сильных связей. Поэтому нужно всегда помнить, что self нужно передавать через слабую ссылку, иначе экземпляр шеф-повара навсегда свяжется с экземпляром официанта, что приведет к утечке памяти даже после удаления этих объектов.
Поэтому последний штрих будет добавить [weak self] перед in и аргументами в передающем замыкании. Всегда помните об этом, пожалуйста!
На этом у меня все. Надеюсь, вам было полезно. Успехов в изучении!