Home > Software engineering >  Cancel a running task when leaving view
Cancel a running task when leaving view

Time:07-28

I am using .task to fetch some data when entering a new view. I do this till it gets the data. This works fine if the call really returns the right data, if not and I close the view, I get an endless loop. This is my code:

VStack(){
        Form{
            Section("QR-Code"){
                if let cgImage = EFQRCode.generate(for: deviceId, watermark: UIImage(named: "LogoSmarterSoftware")?.cgImage) {
                    Image(uiImage: UIImage(cgImage: cgImage)).resizable().frame(width: 150, height: 150)
                }
                /*Button("Login"){
                    //deviceId = "0B6B03F3-99E1-469F-BE1E-EA163EFE0963"
                }*/
            }
        }.frame(height: 180)
        
    }.task {
        users.removeAll()
        if (deviceId == "") {
            deviceId = x // Could change here
        }
        userId = ""
        employeeId = ""
        /*Task{
            await doHTTPUserCall()
        }*/
        while userId.isEmpty || employeeId.isEmpty{
            Task{
                await doHTTPUserCall()
            }
            print("test")
            do{
                try await Task.sleep(nanoseconds: 3_000_000_000)
            } catch {
                print(error)
            }
            //deviceId = "0B6B03F3-99E1-469F-BE1E-EA163EFE0963"
        }
        
        
    }

If I exit the view without getting a right response, I get an endless loop saying

CancellationError() test

CancellationError() test

CancellationError() test

CancellationError() test

Is there any way of killing an existing task or am I missing something in my code?

CodePudding user response:

There is endless loop there. When view disappears task is cancelled, but cancelation handling is on our side

According to doc:

demo1

so it is needed at least to verify state, like

while !Task.isCancelled && (userId.isEmpty || employeeId.isEmpty) {

or call checkCancellation inside loop wrapping everything in try/catch

while userId.isEmpty || employeeId.isEmpty {
   try Task.checkCancellation()

CodePudding user response:

The problem is your while loop, which catches the thrown error prints it, and then proceeds, effectively ignoring the error after it prints it:

while userId.isEmpty || employeeId.isEmpty{
    Task{
        await doHTTPUserCall()
    }
    print("test")
    do{
        try await Task.sleep(nanoseconds: 3_000_000_000)
    } catch {
        print(error)
    }
    //deviceId = "0B6B03F3-99E1-469F-BE1E-EA163EFE0963"
}

Two solutions:

  1. You can catch and rethrow the error:

    while userId.isEmpty || employeeId.isEmpty{
        Task{
            await doHTTPUserCall()
        }
        print("test")
        do{
            try await Task.sleep(nanoseconds: 3_000_000_000)
        } catch {
            print(error)
            throw error
        }
        //deviceId = "0B6B03F3-99E1-469F-BE1E-EA163EFE0963"
    }
    
  2. Don’t catch the error at all:

    while userId.isEmpty || employeeId.isEmpty{
        Task{
            await doHTTPUserCall()
        }
        print("test")
        try await Task.sleep(nanoseconds: 3_000_000_000)
        //deviceId = "0B6B03F3-99E1-469F-BE1E-EA163EFE0963"
    }
    
  • Related