Как отменить Task в Swift?

Ответ

Да, Task можно отменить, используя механизм CancellationToken.

Основной подход:

  1. Создайте CancellationTokenSource.
  2. Передайте cancellationToken в задачу.
  3. Задача должна периодически проверять статус отмены.
  4. Вызовите cancel() для инициации отмены.

Пример создания и отмены задачи:

let tokenSource = CancellationTokenSource()
let task = Task {
    // Периодически проверяем отмену
    for i in 1...10 {
        try Task.checkCancellation() // Выбрасывает CancellationError при отмене
        print("Processing item (i)")
        try await Task.sleep(nanoseconds: 1_000_000_000)
    }
}

// Через 3 секунды отменяем задачу
Task {
    try await Task.sleep(nanoseconds: 3_000_000_000)
    tokenSource.cancel()
    print("Task cancellation requested")
}

Ключевые моменты:

  • Отмена кооперативная — задача должна явно проверять Task.isCancelled или вызывать Task.checkCancellation().
  • Состояние задачи — после отмены task.isCancelled будет true, но task.isCompleted станет true только после завершения работы задачи.
  • Очистка ресурсов — используйте defer или Task.checkCancellation() для корректного освобождения ресурсов при отмене.

Проверка отмены вручную:

Task {
    while !Task.isCancelled {
        // Выполняем работу
        await processNextItem()
    }
    // Выполняем финализацию
    cleanupResources()
}

Ответ 18+ 🔞

А, ну так вот, слушай, про отмену задач в этих ваших асинхронных делах. Всё просто, как три копейки, но есть нюансы, блядь.

Короче, главный инструмент — это CancellationTokenSource. Представь его как такую красную кнопку, на которую можно нажать и сказать «всё, пиздец, заканчивай».

Как это работает, по шагам:

  1. Создаёшь себе эту самую «кнопку» — CancellationTokenSource.
  2. Берёшь у неё токен и суёшь его в свою задачу.
  3. Внутри задачи ты должен быть не лох и периодически спрашивать: «А не отменили ли меня, случайно?»
  4. Когда захочешь всё остановить — просто жмёшь cancel() на этой кнопке. Всё, приехали.

Вот смотри, как это выглядит в коде, ёпта:

let tokenSource = CancellationTokenSource() // Вот она, наша волшебная кнопка
let task = Task {
    // Делаем что-то полезное, но с оглядкой
    for i in 1...10 {
        try Task.checkCancellation() // Спрашиваем: "Меня не выебали?" Если да — вылетает ошибка.
        print("Обрабатываю элемент (i)")
        try await Task.sleep(nanoseconds: 1_000_000_000) // Спим секунду
    }
}

// А вот отдельная задачка, которая через 3 секунды нажмёт на красную кнопку
Task {
    try await Task.sleep(nanoseconds: 3_000_000_000)
    tokenSource.cancel() // Бдыщ! Отмена!
    print("Запросили отмену задачи, пиздец")
}

Важные моменты, которые надо понимать, чтобы не обосраться:

  • Отмена — она кооперативная, сука. Это не как в армии — «раз! и всё». Задача сама должна быть умной и проверять, не пора ли ей сворачиваться. Если она будет тупо молотить цикл без проверок, то хоть обосци токеном — она не остановится.
  • Состояние задачи. После вызова cancel(), у задачи isCancelled станет true. Но isCompleted станет true только когда она реально завершит свою работу (или вылетит по ошибке отмены).
  • Уборка за собой. Всегда думай об очистке ресурсов. Используй defer или лови ошибку отмены, чтобы закрыть файлы, сетевые соединения и прочую хуйню. Не будь свиньёй.

Можно и вручную проверять, если не хочешь, чтобы тебя выкидывало с ошибкой:

Task {
    // Работаем, пока не прилетит команда "стоп"
    while !Task.isCancelled {
        // Делаем что-то тяжёлое
        await processNextItem()
    }
    // А вот тут уже делаем финальные штрихи, типа "всё, я закончил, можно убирать инструменты"
    cleanupResources()
}

Вот и вся магия. Главное — не забывай про эти проверки, а то будет как в том анекдоте: «запустил задачу, а она до сих пор бежит, ебать её в сраку».