Observable.Retry 没有按预期工作 [英] Observable.Retry doesn't work as expected

查看:34
本文介绍了Observable.Retry 没有按预期工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用异步方法处理的数字序列.我正在模拟一个可能失败的远程服务调用.如果失败,我想重试,直到调用成功.

I have a sequence of numbers that are processed using an async method. I'm simulating a remote service call that may fail. In case of failure, I would like to retry until the call successes.

问题在于,对于我正在尝试的代码,每次在异步方法中抛出异常时,序列似乎永远挂起.

The problem is that with the code I'm trying, every time an exception is thrown in the async method, the sequence seems to hang forever.

您可以使用这个简单的代码片段进行测试(已在 LINQPad 中进行了测试)

You can test it with this simple code snippet (it's tested in LINQPad)

Random rnd = new Random();

void Main()
{
    var numbers = Enumerable.Range(1, 10).ToObservable();
    var processed = numbers.SelectMany(n => Process(n).ToObservable().Retry());
    processed.Subscribe( f => Console.WriteLine(f));
}

public async Task<int> Process(int n)
{
    if (rnd.Next(2) == 1)
    {
        throw new InvalidOperationException();
    }

    await Task.Delay(2000);
    return n*10;    
}

它应该处理每个元素,重试失败的元素.相反,它永远不会结束,我不知道为什么.

It should process every element, retrying the ones that have failed. Instead, it never ends and I don't know why.

我怎样才能让它做我想做的事?

How can I make it to do what I want?

(感谢@CharlesNRice 和@JonSkeet 提供线索!):

(thanks @CharlesNRice and @JonSkeet for the clues!):

这有效!

Random rnd = new Random();

void Main()
{
    var numbers = Enumerable.Range(1, 10).ToObservable();
    var processed = numbers.SelectMany(n => RetryTask(() => MyTask(n)).ToObservable());
    processed.Subscribe(f => Console.WriteLine(f));
}

private async Task<int> MyTask(int n)
{
    if (rnd.Next(2) == 1)
    {
        throw new InvalidOperationException();
    }

    await System.Threading.Tasks.Task.Delay(2000);
    return n * 10;
}

async Task<T> RetryTask<T>(Func<Task<T>> myTask, int? retryCount = null)
{
    while (true)
    {
        try
        {
            return await myTask();
        }
        catch (Exception)
        {
            Debug.WriteLine("Retrying...");


            if (retryCount.HasValue)
            {
                if (retryCount == 0)
                {
                    throw;
                }

                retryCount--;
            }
        }
    }
}

推荐答案

滚动你自己的 Retry 在这种情况下是过度的.您可以通过简单地将您的方法调用包装在 Defer 块中来实现相同的目的,并且会在重试发生时重新执行.

Rolling your own Retry is overkill in this case. You can achieve the same thing by simply wrapping your method call in a Defer block and it will be re-executed when the retry occurs.

var numbers = Enumerable.Range(1, 10).ToObservable();

var processed = numbers.SelectMany(n => 
  //Defer call passed method every time it is subscribed to,
  //Allowing the Retry to work correctly.
  Observable.Defer(() => 
    Process(n).ToObservable()).Retry()
);

processed.Subscribe( f => Console.WriteLine(f));

这篇关于Observable.Retry 没有按预期工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆