How to not freeze the UI, and wait for response?

2020-08-01 06:53发布

问题:

I've been trying since the morning but I didnt achieve what I wanted.

I tried DispatchQueue.main.async and completion block but my "Submit" button in the UI still freezes waiting for the data to be returned from the server. This is my code:

func createData(request:Crudpb_CreateRequest, with completion: @escaping (String) -> Void) throws {
    DispatchQueue.main.async {
        self.response = try! self.client.create(request) // <---- How to handle error for this server call when the server is not available or is down?
        completion(self.response.result)
    }
}

I just noticed Im calling the 1st method from the following which is a Synchronous Unary which might be the reason behind the problem. But again I dont know how to call the second function in the fallowing:

 /// Synchronous. Unary.
  internal func create(_ request: Crudpb_CreateRequest, metadata customMetadata: Metadata) throws -> Crudpb_CreateResponse {
    return try Crudpb_CrudServiceCreateCallBase(channel)
      .run(request: request, metadata: customMetadata)
  }
  /// Asynchronous. Unary.
  @discardableResult
  internal func create(_ request: Crudpb_CreateRequest, metadata customMetadata: Metadata, completion: @escaping (Crudpb_CreateResponse?, CallResult) -> Void) throws -> Crudpb_CrudServiceCreateCall {
    return try Crudpb_CrudServiceCreateCallBase(channel)
      .start(request: request, metadata: customMetadata, completion: completion)
  }

Server Side Code:

func (*server) Create(ctx context.Context, req *crudpb.CreateRequest) (*crudpb.CreateResponse, error) {

    var result string
    firstName := req.GetAccount().GetFirstName()
    lastName := req.GetAccount().GetLastName()
    //  id := gocql.TimeUUID()

    fmt.Println("Triggered CREATE function on Go Server " + firstName + " " + lastName + "! Yayy!")

    result = fmt.Sprintf("id for %s %s : %s", firstName, lastName, strconv.Itoa(rand.Intn(100)))
    return &crudpb.CreateResponse{
        Result: result,
    }, nil

I need this app / submit button not to freeze while it fetches result from server.

Please help.

回答1:

You are still performing work on the main thread.. async only makes the createData() method to return before the task is completed.. but the task will still be processed at some time in the main thread and during this time your application will become unresponsive.. try using a global queue instead.. to keep your main thread clean..

Dont forget to perform all your UI work on the main thread after getting your response.



回答2:

Use the asynchronous function instead and call the completion block inside create function's completion.

func createData(request:Crudpb_CreateRequest, with completion: @escaping (String) -> Void) throws { 
try! self.client.create(request) { (response: Crudpb_CreateResponse?, result: CallResult) in 
  DispatchQueue.main.async {
// This is assuming your completion involves UI operations. Otherwise there is no need for this async call.

let stringOutput = String(data: result.resultData!, encoding: String.Encoding.utf8))
    completion(stringOutput)
    }
  }
}


回答3:

Remove DispatchQueue.main.async block from the createData method

func createData(request:Crudpb_CreateRequest, with completion: @escaping (String) -> Void) throws {
    self.response = try! self.client.create(request)
    completion(self.response.result)
}

Use main queue only where you update the UI from the api response

myobj.createData(request: request, with: { string in
    print(string)//background thread
    DispatchQueue.main.async {
        self.label.text = sting//main thread
    }
})


回答4:

The UI freeze because you are doing too much work on the main thread. You should find out what function blocks the main thread. The instruments time profiler is an easy way to see which function is spending too much time.