Suspend function — то је функција са модификатором suspend која може да суспендује своје извршење без блокирања нити и да се касније настави у истој корутини. Према JetBrains Kotlin Docs, 2025, suspend-функције су фундаментални градивни блок корутина, обезбеђујући асинхроност без callback-ова. Свака suspend-функција се компилира у state-машину засновану на Continuation, што омогућава ефикасно управљање тачкама суспензије.
Главне тачке
Suspend function — то је функција декларисана кључном речи suspend која може да суспендује извршење у једној или више тачака без блокирања нити. Сваки позив suspend-функције унутар друге suspend-функције је потенцијална тачка суспензије.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
Kotlin компајлер преводи такву функцију у state-машину. Свака тачка суспензије (позив друге suspend-функције) постаје стање (label). Тренутна нит се ослобађа између стања, а након завршетка очекиване операције, извршење се наставља од следећег стања.
Suspend-функције су се појавиле у Kotlin 1.3 (2018. година) заједно са корутинама као експериментална функција и постале стабилне у Kotlin 1.5 (2021. година). Пре тога, асинхроност у Kotlin/Java-и је обезбеђивана кроз callback-ове, RxJava и CompletableFuture. Suspend-функције су понудиле алтернативу са линеарном синтаксом и аутоматским управљањем нитима.
Разумевање унутрашње структуре suspend-функција је кључ за правилан рад са корутинама. За разлику од обичних функција, свака suspend-функција се компилира у класу са Continuation интерфејсом.
Kotlin компајлер додаје параметар типа Continuation на крај сваког параметра suspend-функције. Continuation садржи:
Претпоставимо да имамо suspend-функцију са два позива других suspend-функција:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
Компајлер је претвара у state-машину са ознакама:
// Поједностављена репрезентација генерисаног кода
fun process(cont: Continuation<Unit>): Any? {
val cont = cont as ProcessContinuation
when (cont.label) {
0 -> {
cont.label = 1
if (stepOne(cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
1 -> {
cont.label = 2
val a = cont.result as TypeA
if (stepTwo(a, cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
2 -> {
println(cont.result)
Unit
}
}
}
Кључно запажање: ако функција враћа COROUTINE_SUSPENDED, тренутна нит се ослобађа. Када се асинхрона операција заврши, позива се Continuation.resumeWith, а state-машина наставља од следећег label-а.
Декларација suspend-функције се не разликује од обичне, осим кључне речи suspend пре fun. Постоји само једно ограничење: suspend-функција се може позвати само из корутине или друге suspend-функције.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
У овом примеру delay је такође suspend-функција која зауставља корутину на одређени број милисекунди без блокирања нити. Након паузе, извршење се наставља.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
runBlocking ствара мост између обичног света и корутина. Унутар lambda-е дозвољен је позив било којих suspend-функција.
Kotlin подржава suspend верзије функционалних типова — suspend () -> T и suspend (A) -> B. Ово омогућава прослеђивање асинхроних lambda-а функцијама вишег реда:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
Функција withRetry прима suspend lambda-у и понавља њено извршење при грешкама. Ово је типичан образац за мрежне захтеве са понављањем.
Разлике између suspend и обичних функција превазилазе просто додавање модификатора. Размотримо суштинске разлике.
| Карактеристика | Обична функција | Suspend-функција |
|---|---|---|
| Нит извршења | Блокира нит до завршетка | Може ослободити нит и наставити касније |
| Параметри компајлера | Само наведени параметри | Имплицитни Continuation на крају |
| Позив из обичне функције | Да | Не |
| Стек | Физички стек нити | State-машина на heap-у + физички стек између тачака |
| Повратак | Непосредна вредност | Вредност или COROUTINE_SUSPENDED |
| Перформансе | Минимални додатни трошкови | ~неколико наносекунди за state-машину (Kotlin 1.9+) |
Обична функција нема Continuation — нема где да сачува стање и чиме да настави извршење. Ако треба да позовете suspend-функцију из обичне, користите runBlocking (за тестове) или CoroutineScope.launch (за продукцију узимајући у обзир lifecycle).
Размотримо три реална сценарија коришћења suspend-функција у Android апликацијама на Kotlin-у.
Room директно подржава suspend-функције — упит се извршава у позадини аутоматски:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
Room интерно користи Dispatchers.IO за извршење упита, а резултат се враћа диспечеру у коме је suspend-функција позвана.
class ProfileViewModel : ViewModel() {
private val repo = ProfileRepository()
fun loadProfile(id: String) {
viewModelScope.launch {
val profile = repo.getProfile(id)
_profile.value = profile
}
}
}
ViewModelScope.launch ствара корутину унутар које се позива suspend-функција getProfile. Након добијања резултата, UI се ажурира на главној нити.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Три suspend-функције се извршавају секвенцијално. На сваком кораку корутина може да се заустави без блокирања нити. Ако неки корак баци изузетак — остали се не извршавају, што штити од неисправних стања поруџбине.
Чак и искусни Kotlin програмери праве грешке при пројектовању suspend-функција. Размотримо најчешће.
Suspend-функција не чини код аутоматски асинхроним. Thread.sleep(), InputStream.read() и други блокирајући позиви и даље ће блокирати нит. Користите withContext(Dispatchers.IO) за облагање блокирајућих операција.
Ако функција не позива друге suspend-функције и не извршава асинхроне операције — модификатор suspend је сувишан. Додаје додатне трошкове за state-машину и ограничава контекст позива. Направите функцију suspend само када се стварно суспендује.
При отказивању корутине, suspend-функције бацају CancellationException. Не хватајте га без размишљања — лишавате позивајући код могућности да правилно заврши отказивање. Ако треба да извршите финализујућу операцију, користите finally блок и NonCancellable.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
Блок finally се увек извршава, укључујући отказивање, а NonCancellable гарантује да се чишћење неће прекинути.
Не може се позвати suspend-функција директно из callback-а без стварања корутине. Користите suspendCoroutine или suspendCancellableCoroutine за адаптацију callback стила корутинама.
Често постављана питања
Да, технички suspend-функција може да не позива друге suspend-функције. Компајлер ће створити state-машину са једним стањем (label 0). Међутим, практичне користи од такве функције нема — извршава се као обична, али са додатним трошковима. Не користите suspend без потребе.
Kotlin пружа kotlinx-coroutines-debug — библиотеку са DebugProbes и алатима за праћење корутина. У Android Studio од верзије Arctic Fox постоји уграђена картица Coroutines у Debugger-у која приказује активне корутине, њихово стање и тачке суспензије.
Свака тачка суспензије ствара ново стање у state-машини. За већину апликација додатни трошак једне тачке износи јединице наносекунди (Kotlin 1.9+). Само код десетина хиљада тачака у петљи вреди комбиновати операције или користити sequence/flow.
У Kotlin-у suspend је модификатор типа функције, а не ознака повратне вредности (као async у C#). Било која suspend-функција може имати произвољне параметре и повратни тип, а њен позив се синтаксно не разликује од позива обичне функције — не постоји await оператор на месту позива.
Користите suspendCancellableCoroutine за адаптацију. Унутра прослеђујете регистрацију callback-а која позива continuation.resume() и враћате токен отказивања ако callback подржава одјаву. Ово је стандардни образац за облагање старих Android API-ја.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође