【问题标题】:How do I wait for the callback to finish before I return the value?如何在返回值之前等待回调完成?
【发布时间】:2020-04-17 11:13:12
【问题描述】:
public List<S3Object> ListObjects(string bucketname, string identityId)
{
    List<S3Object> objectList = new List<S3Object>();

    var request = new ListObjectsV2Request()
    {
        BucketName = bucket_name,
        Prefix = identityId
    };

    Client.ListObjectsV2Async(request, (responseObject) =>
    {
        if (responseObject.Exception == null)
        {
            List<S3Object> list = new List<S3Object>();
            responseObject.Response.S3Objects.ForEach((o) =>
            {
                objectList.Add(o);
            });
        }
        else
        {
            Debug.Log(responseObject.Exception);
        }
    });
    //return objectList;         

    // return the object after the callback is finished, e.g. added all the objects into objectList;
    // ListObjectsV2Async method returns void, so it is not possible to use async and await. i guess?
}

我想在 ListObjectsV2Async 方法中回调完成后返回 objectList 字段。但是,这个方法是异步运行的,所以如果我只返回列表,它将返回一个空列表。那么如何等到回调将所有对象添加到列表中才返回呢?

【问题讨论】:

  • 用}).GetAwaiter().GetResult();替换});
  • 对于 Unity,该操作不将 CancellationToken 作为参数,而是将 AmazonServiceCallback 和 AsyncOptions 作为附加参数。它返回无效。

标签: c# unity3d amazon-s3


【解决方案1】:

TaskCompletionSource 可用于捕获回调的结果,从而使方法处于可等待状态。

例如

public Task<List<S3Object>> ListObjectsAsync(string bucket_name, string identityId) {
    TaskCompletionSource<List<S3Object>> tcs = new TaskCompletionSource<List<S3Object>>();

    var request = new ListObjectsV2Request() {
        BucketName = bucket_name,
        Prefix = identityId
    };

    Client.ListObjectsV2Async(request, (responseObject) => {
        if (responseObject.Exception == null) {
            tcs.TrySetResult(responseObject.Response.S3Objects.ToList());
        } else {
            Debug.Log(responseObject.Exception);
            tcs.TrySetException(responseObject.Exception);
        }
    });

    // return the object after the callback is finished
    return tcs.Task;
}

现在允许

List<S3Object> objectList = await myClient.ListObjectsAsync("bucket_name_here", "identityId_here");

【讨论】:

  • 不错!我不知道TaskCompletionSource 存在。看起来像 C# 的承诺。你会建议在调用方法中做什么来检查异常/错误?
  • @Ruzihm 等待的调用可以像平常一样包裹在 try-catch 中。如果存在异常,请注意在回调中如何将异常提供给完成源
【解决方案2】:

如果您确定不想异步执行操作,那么为什么要调用异步版本?请改用ListObjectsV2:

public List<S3Object> ListObjects(string bucketname, string identityId)
{
    List<S3Object> objectList = new List<S3Object>();

    var request = new ListObjectsV2Request()
    {
        BucketName = bucket_name,
        Prefix = identityId
    };
    
    ListObjectsV2Response responseObject = Client.ListObjectsV2(request);
    
    if (responseObject.Exception == null)
    {
        List<S3Object> list = new List<S3Object>();
        responseObject.Response.S3Objects.ForEach((o) =>
        {
            objectList.Add(o);
        });
    }
    else
    {
        Debug.Log(responseObject.Exception);
    }
    
    return objectList; 
}

Nkosi's answer 有一个比本节更优雅的解决方案。如果您想异步获取对象列表,请查看他们的答案。

但是,我建议重新设计,以便 ListObjects 返回 void,接受一个或多个 Action&lt;List&lt;S3Object&gt;&gt; 参数,并在回调中调用适当的操作:

public void ListObjects(string bucketname, string identityId,
        Action<List<S3Object>> callbackSuccess, Action callbackFailure)
{
    List<S3Object> objectList = new List<S3Object>();

    var request = new ListObjectsV2Request()
    {
        BucketName = bucket_name,
        Prefix = identityId
    };
    
    Client.ListObjectsV2Async(request, (responseObject) =>
    {
        if (responseObject.Exception == null)
        {
            List<S3Object> list = new List<S3Object>();
            responseObject.Response.S3Objects.ForEach((o) =>
            {
                objectList.Add(o);
            });
            callbackSuccess.Invoke(objectList);
        }
        else
        {
            Debug.Log(responseObject.Exception);
            callbackFailure.Invoke();
        }
    });
}

然后,在调用ListObjects 的地方,您可以在处理程序中包含您需要的任何局部变量。比如:

int localVar1 = 123;
float localVar2 = 4.56f;

Action<List<S3Object>> handleSuccess = (resultList) => 
{
    DoStuffWithList(resultList, localVar1, localVar2);
};
Action handleFailure = () =>
{
    DoStuffWhenFail(localVar1);
};

s3Client.ListObjects("foo", "bar", handleSuccess, handleFailure);

【讨论】:

    猜你喜欢
    • 2015-09-22
    • 2019-06-11
    • 1970-01-01
    • 2016-05-08
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 2021-12-02
    相关资源
    最近更新 更多