Ось готовий урок, створений спеціально за твоїм запитом, у стилі CS50.
🎓 CS50: Callbacks та Workflow-оркестрація
(або "Як змусити код дзвонити вам, а не чекати під дверима")
Привіт, друзі! 👋
Уявіть, що ви прийшли до популярної піцерії у п'ятницю ввечері. Там черга, шум, гам. Ви замовляєте свою улюблену "Пепероні". Що відбувається далі?
Ви ж не стоїте біля касира, втупившись йому в очі й питаючи кожні 3 секунди: "Вже готова? А зараз? А тепер?" Це було б дивно, неефективно і, чесно кажучи, трохи моторошно.
Натомість касир дає вам пейджер (або "шайбу"), яка завібрує і заблимає, коли замовлення буде готове. Ви йдете сісти за столик, перевіряєте Instagram, спілкуєтесь із друзями. Ви делегували очікування.
Чому це важливо для нас, програмістів? Тому що в коді ми часто робимо ту саму помилку "настирливого клієнта". Ми пишемо код, який блокує все виконання, чекаючи на результат. Або ми пишемо величезні, заплутані сценарії (workflow), які ламаються від найменшої зміни.
Сьогодні ми розберемося з двома суперсилами: 1. Callbacks (Колбеки) — це ваші цифрові "пейджери". 2. Workflow-оркестрація — це менеджер піцерії, який знає, в якому порядку тісто йде в піч, а пейджер — до клієнта.
Без цього сучасні веб-додатки, AI-агенти та складні системи просто завмерли б. Поїхали! 🚀
1. 🔥 Вступ: Проблема та мотивація
Давайте подивимось на проблему "в лоб". Уявіть, що ви пишете програму для обробки зображень.
- Завантажити фото.
- Накласти фільтр.
- Зберегти.
- Надіслати користувачу email.
Якщо писати це "в лоб" одним шматком, то поки фото завантажується (а це може бути 5 секунд), ваша програма зависає. Інтерфейс не реагує. Користувач думає, що все зламалося, і люто клікає мишкою.
Питання до вас: Як зробити так, щоб програма сказала: "Я почала завантаження, займайся своїми справами, а коли закінчу — я тебе покличу"?
Відповідь — Callback. Це функція, яку ми передаємо іншій функції як інструкцію: "Виконай це, коли закінчиш свою роботу".
А якщо кроків стає 10? А якщо крок №3 залежить від кроку №1, але тільки якщо крок №2 пройшов успішно? Тут вступає Оркестрація.
2. 🧠 Теоретична база (без нудних лекцій)
Давайте розкладемо це на атоми.
Що таке Callback (Зворотний виклик)?
Простими словами: це функція, яку ви передаєте як аргумент іншій функції.
Аналогія: Ви даєте другу свій номер телефону (callback) і кажете: "Подзвони мені, коли будеш під моїм під'їздом". Ви не чекаєте на балконі 2 години. Ви займаєтесь своїми справами. Номер телефону — це посилання на функцію. Дзвінок — це виконання функції.
Що таке Оркестрація (Workflow)?
Це логіка, яка керує послідовністю виконання завдань.
Аналогія: Це рецепт або диригент оркестру. Диригент не грає на скрипці. Він показує скрипалю, коли почати, а барабанщику — коли вступити. Оркестратор каже: "Спочатку функція А, якщо успішно — то функція Б (з колбеком), інакше — функція В".
Як це працює "під капотом"?
У більшості мов (Python, JavaScript) функції — це "громадяни першого сорту". Це означає, що функція — це така сама змінна, як число 5 або рядок "hello".
* Ви можете покласти функцію в змінну.
* Ви можете передати функцію в іншу функцію.
* Комп'ютер просто передає адресу в пам'яті, де лежить код цієї функції.
📌 Запам'ятайте головне: Ви не викликаєте функцію одразу (не ставите дужки ()). Ви просто передаєте її ім'я.
3. 🧪 Приклади (Python edition)
Рівень 1: Найпростіший Callback
Давайте напишемо функцію, яка вітається, але "стиль" вітання ми передамо ззовні.
# 1. Визначаємо допоміжні функції (наші колбеки)
def shout(text):
return text.upper() + "!!!"
def whisper(text):
return text.lower() + "..."
# 2. Основна функція, яка приймає колбек
# Зверніть увагу: formatter - це змінна, яка стане функцією
def greet(name, formatter):
message = f"Привіт, {name}"
# Тут ми викликаємо передану функцію
result = formatter(message)
print(result)
# Тестуємо
# Питання: Що виведе цей код?
greet("Студент", shout)
# Очікування: ПРИВІТ, СТУДЕНТ!!!
greet("Студент", whisper)
# Очікування: привіт, студент...
Бачите? Логіка greet одна й та сама, але поведінка змінюється залежно від того, яку "інструкцію" ми передали.
Рівень 2: Реальний світ (Імітація завантаження)
У реальних проєктах колбеки часто використовують для сповіщення про завершення довгої операції.
import time
def on_success(filename):
print(f"✅ Ура! Файл '{filename}' успішно завантажено. Можна відкривати шампанське.")
def on_error(filename):
print(f"❌ Халепа. Файл '{filename}' не завантажився. Спробуйте ще раз.")
# Функція-імітатор довгого процесу
def download_file(filename, success_callback, error_callback):
print(f"⏳ Починаю завантаження {filename}...")
time.sleep(2) # Імітуємо затримку
# Симулюємо ситуацію: якщо в назві є "virus", буде помилка
if "virus" in filename:
error_callback(filename)
else:
success_callback(filename)
# Запуск
print("--- Сценарій 1 ---")
download_file("homework.pdf", on_success, on_error)
print("\n--- Сценарій 2 ---")
download_file("super_virus.exe", on_success, on_error)
Чому це круто? Функція download_file не знає, що робити після завантаження. Вона просто каже "Готово" або "Помилка". Це робить її універсальною. Ви можете використати її в консольній програмі (print), або у веб-додатку (popup-вікно).
Рівень 3: Міні-оркестрація (Workflow)
А тепер поєднаємо все. У нас є ланцюжок дій (Workflow), і ми хочемо, щоб вони виконувалися по черзі.
def step_1_prep(data):
print("🧹 Крок 1: Чистимо дані...")
return data.strip()
def step_2_analyze(data):
print(f"🔍 Крок 2: Аналізуємо довжину '{data}'...")
return len(data)
def step_3_report(result):
print(f"📊 Крок 3: Звіт готовий. Результат: {result}")
# Наш Оркестратор
def run_workflow(input_data, steps):
current_value = input_data
for i, step_func in enumerate(steps):
print(f"--- Запуск етапу {i+1} ---")
# Результат попереднього кроку стає вхідними даними для наступного
current_value = step_func(current_value)
print("🏁 Workflow завершено!")
# Визначаємо послідовність (список колбеків)
my_pipeline = [step_1_prep, step_2_analyze, step_3_report]
# Запускаємо
run_workflow(" Hello World ", my_pipeline)
Що тут відбулося? Ми створили конвеєр. run_workflow — це наш менеджер. my_pipeline — це список інструкцій. Ми можемо легко змінити порядок кроків або додати нові, не переписуючи функцію run_workflow.
4. 🛠 Практична частина
Час закачати рукави! Відкривайте IDE або блокнот.
🔹 Завдання 1: Калькулятор на колбеках
Напишіть функцію calculate(a, b, operation), де operation — це функція (додавання, віднімання тощо).
Перевірка: Викличте calculate(10, 5, multiply), де multiply ви написали самі.
🔹 Завдання 2: "Фільтр слів"
Створіть функцію process_text(text, filter_callback). Вона має розбивати текст на слова і застосовувати filter_callback до кожного слова. Якщо колбек повертає True — слово залишається, якщо False — видаляється.
Приклад: Видалити всі слова коротші за 4 букви.
🔹 Завдання 3: Виправ помилку (Code Review)
Студент написав код, але він падає з помилкою TypeError: 'NoneType' object is not callable. Чому?
def my_callback():
print("Готово!")
def do_work(callback):
print("Працюю...")
callback()
# Виклик (тут помилка)
do_work(my_callback())
Підказка: Подивіться на дужки при виклику do_work.
🔹 Завдання 4: Міні-кейс "Розумний будинок" (Оркестрація)
Уявіть, що ви програмуєте сценарій "Я прийшов додому".
Створіть список функцій-кроків: turn_on_lights, play_music, heat_kettle.
Напишіть функцію-оркестратор execute_routine(routine_list), яка виконує їх по черзі.
Додатково: Додайте перевірку: якщо чайник порожній (функція повернула False), зупинити виконання і вивести тривогу.
🔹 Питання "А що, якщо..." Що станеться, якщо колбек, який ми передали, зависне на 10 хвилин? Як це вплине на основну програму? (Подумайте про це, відповідь у наступному розділі).
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в цій темі?
1. Типова помилка новачка: "Callback Hell" (Пекло колбеків) Це коли ви вкладаєте колбек у колбек, у колбек...
step1(arg, callback=lambda x: step2(x, callback=lambda y: step3(y...)))
Виглядає як сходинка вниз. Читати це неможливо.
Як думає профі: "Мені потрібна плоска структура. Я використаю Promise (у JS), async/await або нормальну функцію-оркестратор, як ми робили в прикладі №3".
2. Втрата контексту Новачки часто забувають, що функція-колбек може потребувати доступу до даних, яких немає в момент її виклику. Порада: Передавайте дані явно через аргументи, не покладайтеся на глобальні змінні.
3. Принцип "Single Responsibility" (Єдина відповідальність)
Ваша функція download_file не повинна знати, як показувати помилку (червоним текстом, звуком чи SMS). Вона повинна просто викликати on_error. Як саме обробити помилку — це проблема того коду, який викликав download_file. Це називається Decoupling (слабка зв'язність). Це святий грааль архітектури.
6. 🧩 Підсумок
Ну що, видихнули? Давайте підсумуємо.
Сьогодні ми не просто навчилися передавати функції як змінні. Ми змінили парадигму мислення:
- Callbacks дозволяють коду бути гнучким і реагувати на події, а не чекати їх.
- Оркестрація дозволяє будувати складні процеси з простих, незалежних цеглинок.
- Ви тепер знаєте, як відокремити логіку виконання завдання від логіки обробки результату.
Тепер ви вмієте:
✅ Писати функції, які приймають поведінку (код) як аргумент.
✅ Будувати прості пайплайни обробки даних.
✅ Читати код бібліотек, де суцільні on_click, on_request, handler.
🔮 Тизер наступного уроку:
Ви помітили, що в наших прикладах код все одно виконувався по черзі? time.sleep зупиняв усе. На наступному занятті ми зламаємо час. Ми поговоримо про Асинхронність (Async/Await) — як робити 10 справ одночасно, використовуючи лише одного кухаря.
Це був CS50. Побачимось! 👋