back to article Swift 5.5 unleashed with async keyword to fix 'pyramid of doom', plus other changes in 'massive release'

Apple has emitted version 5.5 of the Swift programming language, described as a "massive release," including async/await keywords, package collections, and improved Objective-C interoperability. The most anticipated new features in Swift 5.5 are around concurrency. One of the issues with Swift's earlier approach to …

  1. Kevin McMurtrie Silver badge

    A sink

    After working in a few programming languages and styles, I can confidently say that most asynchronous designs are antipatterns. There are few times when you call a method but don't need the answer until later. When most calls are synchronous by default, you're adding async wrappers 4% of the time. When they're async by default, you're adding unwrapping or chaining 96% of the time.

    1. TripodBrandy

      Re: A sink

      Every 'await' is essentially a manual yield back to the event loop, where the function stops running and other asynchronous code can be scheduled. It's basically co-operative threading and you are subject to the same kinds of concurrency bugs you would get in a regular multithreaded program with syncronous I/O. It would be better if the language simply provided green threads and made async I/O look like sync I/O, or just use threads.

      1. Robert Grant

        Re: A sink

        I don't see how you could think this. Async/await is pretty safe, as there won't be shared memory and other hideous bug-causing footguns available. It just hands off a single async task and pauses execution until the task completes.

    2. Brewster's Angle Grinder Silver badge

      Re: A sink

      The joy of async/await is that most calls are synchronous and you wrap the asynchronous ones.

      And as noted above, this is sugar for storing the state, returning to the event loop, running more events, and then picking up when the next event happens.

      Sure we did all that by hand in select(2) loops thirty years ago. It is a lot less fuss. And these days a lot more stuff comes from the network or from other threads. And it removes that boiler plate.

    3. Kristian Walsh Silver badge

      Re: A sink

      I disagree.

      The use case for “async” is primarily to prevent blocking-waits within threads, not to pre-compute things. The classic example is networked APIs

      HttpResultThing result = await fetchURL("http://test.com");

      // --- thread yields here until resource is ready.

      if (result.IsOk )

      {

      // do your processing

      }

      Any equivalent without async either busy-waits, or requires all of the plumbing hidden by “await” to be implemented explicitly in the application, usually creating ideal conditions for callback-hell (“the pyramid of doom”) in the process.

      The advantage of having async in the language, and thus the runtime libraries, is that it lowers the cost of doing things the “Right Way”. File I/O, for instance, is a blocking operation, but most C/C++ devs tend to ignore this fact because a call to read() is usually a fast operation... until the files get big, or have to be accessed over a network. A framework where all I/O is asynchronously handled makes applications more responsive to events without needing more threads.

      Your second point is also not correct - async is an optional feature: methods are never async by default, and if the Swift runtime is anything like the C# one I’m familiar with, most methods are not async, unless they can block your thread. For the case of Swift, scopes only gain the async plumbing silently if they include an “await” inside (as it makes no sense to use “await” in a non-async context); no “await”: no async.

      The truth is, if you really care about performance, you write the bits that need that performance in C, where you have much tighter control over the resources at your disposal. If you’re using any high-level language that’s managing memory and tasks for you, you have already tacitly admitted that you do not require maximum performance for your application.

    4. DrXym

      Re: A sink

      I think async / await keywords work really well when I've used them in Rust. The code is asynchronous but the code is clean and readable - call this thing and await for it to return. So the flow is basically synchronous but it yields and the scheduler / executor can be getting on with making progress on something else.

      I'm sure the same is true for other languages that implement these keywords. It's way easier to write and to understand than promises (for example) where you'd be chaining actions onto the end of other actions and it becomes a huge mess.

  2. sabroni Silver badge

    await

    That code is lovely but the first "await"ed call will need to finish before the second starts.

    Is there a way of saying "Run all these, get back to me when they're all done" rather than "do this, then do that, then do that, then do that"?

    1. Ken Y-N
      Go

      Re: await

      Fortunately, there is a simple way, described here:

      https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#ID641

      1. anonanonanonanonanon

        Re: await

        Also in the 5.5 release is structured concurrency, I'm still reading it, but it seems to address what you ask : https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md

    2. Anonymous Coward
      Anonymous Coward

      Re: await

      > That code is lovely but the first "await"ed call will need to finish before the second starts.

      What, really?

      As a non-Swift developer I looked at that example and thought: cool, each await statement runs in parallel and the surrounding code automatically waits at the end of the enclosing block for all of them to finish.

      As that is apparently not the case, I'm now asking myself why not, as that would be a lot more convenient?

      1. DrXym

        Re: await

        The purpose of await is to write code that resembles synchronous code but each await is an opportunity for a scheduler to be making progress on other async tasks. So basically the scheduler will call each async task asking them to make progress in turn, and if they can't they yield and it goes onto the next one.

        It is actually a fantastically powerful construct since it makes async code easy to read while allowing code to make better use of hardware. I use it in Rust and it's a vast improvement over earlier attempts at async using a promise style callback where it can it can very hairy, very fast.

        1. sabroni Silver badge

          Re: and if they can't they yield and it goes onto the next one.

          But if the next one requires the output from the first one (we're writing code like it's synchhronous) then it can't start.

          I think you're right, but the other tasks that are getting processor aren't in this thread, it's letting other threads get a bit of the pie. So it helps you scale and parallelise but doesn't magically run two of those awaited statements at once. They have to complete before the next one can start.

          1. 7layersdown

            Re: and if they can't they yield and it goes onto the next one.

            But you get to decide whether to perform the 2 tasks in parallel (async let both statements and then await both tasks together) or serially (await each task in turn)

    3. J. Cook Silver badge
      Joke

      Re: await

      Is there a way of saying "Run all these, get back to me when they're all done" rather than "do this, then do that, then do that, then do that"?

      Great, now I have Fatboy Slim's "Weapon of Choice" running in my head. (not necessarily a bad thing, though...)

  3. Anonymous Coward
    Anonymous Coward

    re: 'island of serialization in a sea of concurrency'

    Or "Single threaded apartment" as we called them in COM 30 years ago. Plus ca change.....

  4. Quando

    Not (currently) supported before iOS 15

    The big problem with the Swift 5.5 release on Apple platforms is that there is no support for the concurrency changes on OS versions before iOS 15 / Mac OS 12, which, despite the rapid upgrade of most Apple users, still leaves those real world developers whose apps support older OS versions out in the cold. It's fine for hobbyist developers, but in the real world forcing a latest OS minimum is tough - even if 90%+ of active devices will be using it within 6 months, that last few % tend to be *very* vocal.

    There are some comments from internal Apple people on the Swift forums that work is ongoing to back port the changes but no certainty that it will work, or how far back it would offer support. A lot of major third party systems still support back as far as iOS 10.0, so it will be years before they move to a minimum of 15.0 and make this usable, in the meantime they are pushing forward with a Swift 6.0 which sounds like it might also need more OS support and thus be iOS 16 as a base.

    1. DS999 Silver badge

      Re: Not (currently) supported before iOS 15

      iOS 12 would be a pretty reasonable target if they can backport it there. It is still being updated and covers all the 64 bit hardware from the iPhone 5S released eight years ago that iOS 14/15 doesn't.

  5. cyberdemon Silver badge
    WTF?

    my eyes

    wtf is this shit?

    What's wrong with standard languages like ISO C, C++, Python, Javascript, even Rust is more standard than this apple-centric bollocks

    1. gnasher729 Silver badge

      Re: my eyes

      “Apple-centric bollocks” - Swift 5.0 is nice. Swift 5.5 seems will be a little bit nicer. I use it on a Mac, for iOS development, but there’s nothing apple specific in the language.

POST COMMENT House rules

Not a member of The Register? Create a new account here.

  • Enter your comment

  • Add an icon

Anonymous cowards cannot choose their icon

Other stories you might like