Concurrency: async/await

Write asynchronous code that reads like ordinary top-to-bottom code. Learn async / await , launch work with Task , run things in parallel with async let , and keep shared state safe with actors and the @MainActor .

Learn Concurrency: async/await in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ async / await Basics

An async function can suspend ; await marks where it pauses. From ordinary code, wrap async calls in a Task {' '} to create an async context.

2️⃣ Sequential awaits

Plain await calls run one after another — each waits for the previous to finish. That's correct when later work depends on earlier results.

3️⃣ Concurrency with async let

When operations are independent , async let starts them all at once so they overlap. You await the results later — total time is roughly the longest single task, not the sum.

4️⃣ Actors and @MainActor

An actor serializes access to its mutable state, preventing data races. Reaching into an actor from outside is asynchronous, so it needs await . The special @MainActor pins code to the main thread — essential for UI updates.

Your turn. Fill in the async keyword, the suspend keyword, and the Task type.

📋 Quick Reference

No blanks this time — just a brief and an outline. Load three things concurrently with async let and print them together.

Practice quiz

What does marking a function 'async' mean?

  • It runs on the GPU
  • It always runs in parallel
  • It can suspend and resume — pausing without blocking the thread
  • It cannot fail

Answer: It can suspend and resume — pausing without blocking the thread. 'async' marks a function that may suspend at await points, freeing the thread while it waits.

What does the 'await' keyword do?

  • Marks a point where the function may suspend until the async result is ready
  • Blocks the thread until done
  • Starts a new thread
  • Catches an error

Answer: Marks a point where the function may suspend until the async result is ready. 'await' marks a potential suspension point where execution can pause and later resume.

How do you call an async function from non-async (synchronous) code?

  • Just call it directly
  • Use a for loop
  • It is impossible
  • Wrap it in a Task { }

Answer: Wrap it in a Task { }. A Task { } creates an async context so you can call and await async functions from sync code.

What is 'async let' used for?

  • Declaring a constant
  • Starting child tasks that run concurrently, awaited later
  • Looping asynchronously
  • Defining an actor

Answer: Starting child tasks that run concurrently, awaited later. 'async let' kicks off concurrent work immediately and you await the results when you need them.

Compared to sequential awaits, what does 'async let' improve?

  • It lets independent operations run in parallel, reducing total wait time
  • Memory usage only
  • Code safety only
  • Nothing

Answer: It lets independent operations run in parallel, reducing total wait time. Independent async let bindings run concurrently instead of one-after-another, so the total time is roughly the longest one, not the sum.

What problem do actors primarily solve?

  • Slow networking
  • JSON decoding
  • Protecting mutable state from data races by serializing access
  • UI layout

Answer: Protecting mutable state from data races by serializing access. An actor isolates its mutable state so only one task touches it at a time, preventing data races.

Accessing an actor's property or method from outside the actor usually requires what?

  • A force-unwrap
  • await, because the access may suspend
  • A guard statement
  • Nothing special

Answer: await, because the access may suspend. Cross-actor access is asynchronous, so you must await it.

What does the @MainActor attribute guarantee?

  • The code runs on a background thread
  • The code never suspends
  • The code is private
  • The code runs on the main thread/actor — required for UI updates

Answer: The code runs on the main thread/actor — required for UI updates. @MainActor ensures the annotated code runs on the main actor, which is where UI work must happen.

Inside an async function, how do you await two independent calls so they overlap?

  • await a(); await b()
  • async let x = a(); async let y = b(); let r = await (x, y)
  • Task { a() }; Task { b() }
  • guard let

Answer: async let x = a(); async let y = b(); let r = await (x, y). async let starts both concurrently; awaiting the tuple lets them overlap rather than run sequentially.

Why is async/await preferred over nested completion-handler callbacks?

  • It is the only option
  • It is faster on the CPU
  • It reads top-to-bottom like synchronous code, avoiding callback nesting
  • It removes the need for error handling

Answer: It reads top-to-bottom like synchronous code, avoiding callback nesting. async/await flattens asynchronous logic into straight-line, readable code instead of pyramids of callbacks.