Difference between "async let" and "async let await"
Asked Answered
D

3

6

I know that we wait with await and execute a task without need to wait with async let, but I can't understand the difference between these two calls:

async let resultA = myAsyncFunc()
async let resultB = await myAsyncFunc()

In my experiment, both of these seem to behave exactly the same, and the await keyword does not have any effects here, but I'm afraid I'm missing something.

Thanks in advance for explanation on this. 🙏🏻


Update

I'm adding a working sample so you can see the behavior

func myAsyncFuncA() async -> String {
    print("A start")
    try? await Task.sleep(for: .seconds(6))
    return "A"
}

func myAsyncFuncB() async -> String {
    print("B start")
    try? await Task.sleep(for: .seconds(3))
    return "B"
}

async let resultA = myAsyncFuncA()
async let resultB = await myAsyncFuncB()
print("Both have been triggered")
await print(resultA, resultB)

Results:

A start                   // Immediately
B start                   // Immediately
Both have been triggered  // Immediately
A B                       // After 6 seconds

So as you can see, resultA does not block the context and the total waiting time is the biggest waiting time.

Debark answered 8/3 at 7:32 Comment(0)
M
7

You asked what is the “difference between ‘async let’ and ‘async let await’”. There is none.

The await is unnecessary at this point and is generally omitted. One can argue that in async let x = await … declaration, the await would best be omitted, to avoid confusion, because it does not actually await at that point.

So, the behavior you outline in your revision to your question is correct.

async let resultA = myAsyncFuncA()       // execution of this current task is not suspended
async let resultB = await myAsyncFuncB() // execution is also not suspended here
print("Both have been triggered")        // we can see this before the above two child tasks finish
await print(resultA, resultB)            // only here is this task suspended, awaiting those two child tasks

When resultA and resultB are declared with async let, the respective asynchronous child tasks will be created, but the current task will not be suspended (notably, despite the await in async let resultB = await …). Execution of the current task continue to proceed to the subsequent lines, after those two initializations, while A and B run concurrently. Execution will not actually await the results until you hit the await on the fourth line, the await print(…). The await in the second line, where you async let resultB = await …, does not actually await it.

In SE-0317 – async let bindings, they say that the “initializer of a async let permits the omission of the await keyword”. Describing this as “permits” is an understatement; we practically always omit the await at the point of initialization.

Montford answered 8/3 at 16:4 Comment(2)
Thanks Rob, makes sense. I was expecting the compiler to at least show a warning like when we use a redundant access level modifier.Debark
Agreed. The fact that the compiler allows async let x = await …, where it does not await, defies all expectations.Montford
R
-2
async let resultA = myAsyncFunc()
async let resultB = await myAsyncFunc() 

look similar, but there is a crucial difference between the two.

async let resultA = myAsyncFunc(): 

This syntax starts the asynchronous function myAsyncFunc(), but does not wait for it to complete. It continues immediately with the execution of the next lines of code. The variable resultA is assigned a task that represents the current asynchronous operation, not the actual result of the operation.

async let resultB = await myAsyncFunc(): 

This syntax also starts the asynchronous function myAsyncFunc(), but the await keyword is used to pause the execution of the current task until myAsyncFunc() is completed. As soon as myAsyncFunc() is completed, resultB is assigned the actual result of the asynchronous operation. The main difference lies in the behaviour of the code after these lines:

With async let resultA = myAsyncFunc(), the code continues executing immediately after the asynchronous operation starts, which can lead to simultaneous execution of subsequent tasks.

With async let resultB = await myAsyncFunc(), the code pauses until the asynchronous operation is completed, which ensures that subsequent code is only executed after the asynchronous operation has been completed.

Ridgley answered 8/3 at 7:57 Comment(4)
So basically, async let x = await ... defeats the purpose of async let, which is running multiple tasks in parallel.Appetency
Thanks for the answer. If I understand you correctly, you are saying that async let await is basically the same with let await. But if I can see that unlike let await, the async let await does not pause the block. What am I missing here again?Debark
I have added a sample to show that what am I trying to say.Debark
@Appetency – No, this answer is incorrect. With async let x = await …, it is the await that is ignored.Montford
R
-2

If you use await with async let, the execution of the current task is paused until the expected asynchronous operation is completed, but the subsequent code after this line is actually executed concurrently. This is because Swift concurrency allows tasks to be executed independently and using await only affects the point at which the current task is paused and resumed.

So if you place print statements immediately after async let resultB = await myAsyncFunc(), they will actually be executed at the same time as the expected operation. The key difference is that the current task will not continue beyond this line until myAsyncFunc() is completed, but other tasks can continue to be executed in parallel.

Redding answered 8/3 at 8:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.