Site icon AppTractor

Task.sleep() и Task.yield(): в чем разница

В Swift Concurrency мы можем использовать Task.sleep() и Task.yield(), чтобы перевести конкретную задачу в режим ожидания или приостановки в течение определенного периода времени. Оба они выглядят и ведут себя одинаково, но есть несколько существенных различий, о которых следует знать Swift-разработчикам.

Зная эти различия, вы сможете лучше понять, когда и как использовать те или иные функции, а также оптимизировать свой код соответствующим образом. Прежде чем перейти к рассмотрению различий, давайте сначала объясним, что делают эти модификаторы.

Как использовать Task.sleep()

С помощью Task.sleep() вы можете приостановить выполнение задачи на заданный срок:

try await Task.sleep(for: .seconds(5))

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

Если задача будет отменена до окончания сна, эта функция выбросит ошибку CancellationError, которая известна нам по обычной отмене задач.

Когда следует использовать Task.sleep()?

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

func search(_ query: String) {
    /// Cancel any previous searches that might be 'sleeping'.
    currentSearchTask?.cancel()
    
    let newSearchTask = Task {
        do {
            /// Sleep for 0.5 seconds to wait for a pause in typing before executing the search.
            try await Task.sleep(for: .milliseconds(500))
            
            print("Starting to search!")
            
            /// A simplified static result and search implementation.
            searchResults = Self.articleTitlesDatabase
                .filter { $0.lowercased().contains(query.lowercased()) }
        } catch {
            print("Search was cancelled!")
        }
    }
    self.currentSearchTask = newSearchTask
}

Другими распространенными случаями использования являются:

Обязательно учитывайте отмену при использовании Task.sleep(), соблюдая функциональность throw.

Как использовать Task.yield()

Метод Task.yield() приостанавливает выполнение текущей задачи и позволяет выполнить другие задачи:

await Task.yield()

Вы можете использовать метод для приостановки задачи, возможно, во время длительного выполнения. Это позволит другим задачам некоторое время поработать, прежде чем выполнение вернется к этой задаче.

Однако обратите внимание: если текущая задача уже является самой приоритетной в системе, исполнитель немедленно возобновит выполнение этой же задачи. Другими словами, метод Task.yield() может не оказать никакого влияния.

Когда следует использовать Task.yield()?

По моему опыту, существует не так много распространенных сценариев, в которых вам понадобится использовать Task.yield(). В большинстве случаев лучше использовать Task.sleep(). Для меня самым распространенным случаем было написание тестов для асинхронного кода. Я большой поклонник репозитория Swift Concurrency Extras, который демонстрирует использование следующим образом:

func testIsLoading() async {
    /// Run on a serial executor to attempt running all tasks serially.
    await withMainSerialExecutor {
        let model = NumberFactModel(getFact: {
            /// Yield the current task and let other tasks continue first.
            await Task.yield()
            return "\($0) is a good number."
        })

        let task = Task { await model.getFactButtonTapped() }
        
        /// Yield the test so the `getFactButtonTapped()` method gets called.
        await Task.yield()
        XCTAssertEqual(model.isLoading, true)
        XCTAssertEqual(model.fact, nil)

        /// Wait for the task to return its value.
        await task.value
        XCTAssertEqual(model.isLoading, false)
        XCTAssertEqual(model.fact, "0 is a good number.")
    }
}

Использование Task.yield() позволяет тестируемому коду выполнять свою работу и гарантировать, что результаты будут постоянно доступны для проверки. Если у вас есть другой пример, я буду рад узнать!

Различия между Task.sleep() и Task.yield()

Оба метода приостанавливают выполнение, но у них есть несколько ключевых различий.

Метод Task.sleep() приостанавливает выполнение на определенное время, в то время как метод Task.yield() может приостановить выполнение только в том случае, если другие задачи с аналогичным или более низким приоритетом ожидают выполнения. Поэтому длительность приостановки фиксирована только для Task.sleep() и неопределенна для Task.yield().

Метод сна можно прервать через отмену, в то время как Task.yield() только передает управление. Оба метода являются неблокирующими для соответствующих потоков.

Заключение

И Task.sleep(), и Task.yield() приостанавливают выполнение, но делают это по-разному. В большинстве случаев вы будете использовать метод sleep, в то время как метод yield может быть полезен при написании тестов для асинхронных методов.

Если вы хотите узнать больше о Swift Concurrency, обязательно ознакомьтесь с курсом или прочитайте другие статьи о Swift Concurrency.

Спасибо!

Источник

Exit mobile version