C#承诺无限期地循环方法

时间:2019-02-25 03:57:12

标签: c# loops unity3d promise

我正在将C# Promises library用于Unity项目,并且希望无限期地调用承诺代码块。每次兑现承诺后,都应决定是否再次调用它。我在线程JavaScript ES6 promise for loop中找到了针对ES6的 JavaScript 解决方案。

// JavaScript: Function calling itself upon resolution
(function loop(i) {
    if (i < 10) new Promise((resolve, reject) => {
        setTimeout( () => {
            console.log(i);
            resolve();
        }, Math.random() * 1000);
    }).then(loop.bind(null, i+1));
})(0);

您能帮助我将此想法移植到C#中吗?

我目前正在做的是在Then()循环中使用for形成很长的承诺,并在我想退出时拒绝。但这是一个较弱的解决方案。相反,我只想在解决当前的诺言时考虑下一次迭代。

// C#: Forming the sequence before execution
var p = Promise.Resolved();
for (var i = 0; i < 100; i++)
{
    p = p.Then(outcome =>
    {
        if (shouldContinue)
        {
            // return a new Promise
        }
        else
        {
            // return Promise.Reject()
        }
    });
}

编辑:

请查看下面针对PromiseLooper类的答案。

3 个答案:

答案 0 :(得分:0)

首先,分别为“然后”定义函数。然后确保可以从函数访问您的promise变量。最后,设置您的promise。然后在该函数内部而不是外部。

这是VB.net中的一个示例。 (应该不难将其更改为C#)

class Test
    Dim p As RSG.Promise = RSG.Promise.Resolved
    Private Function AndThen() As RSG.Promise
    '//do something ...
        If True Then
            Threading.Thread.Sleep(10000)
            MsgBox("AndThen")
            '//setup next promise here.
            p = p.Then(AddressOf AndThen)
            Return p
        Else
            Return RSG.Promise.Rejected(New Exception)
        End If
    End Function
    Public Sub Test()
        '//our only external call of promise.then
        p.Then(AddressOf AndThen)
    End Sub
End Class

答案 1 :(得分:0)

如果我需要使用异步方法循环,​​我会尝试找到this之类的“对Unity3d的异步支持”。

否则,您需要将“重复一小段时间”链接在一起,类似于在已显示的JS解决方案中的操作方式。我不确定确切的语法(因为我没有Unity3d),但大致应该是

Promise Loop(
      Func<Promise<TResult>> body, 
      Func<bool, TResult> shouldContinue,
      int iterations, Promise chain = null)
{
     chain = chain == null ?? Promise.Resolved() : chain;

     if (iterations == 0)
     { 
           return Promise.Resolved();
     }  

     Promise result = body().Then(outcome => 
     {
         if (shouldContinue(outcome))
             return Loop(body, iterations-1, result));
         else 
             return Promise.Rejected();
     });
}

答案 2 :(得分:0)

我想到了一个可爱的通用PromiseLooper。此类允许

  • 提供一种Func<IPromise<T>>方法来循环
  • 提供Func<T,bool>谓词以退出循环
  • 束缚循环的另一个承诺onResolve
  • 捕获循环方法中引发的异常

这是PromiseLooper类:

// See https://stackoverflow.com/q/54859305/4112088
public class PromiseLooper<T>
{
    private readonly Func<IPromise<T>> loopingBody;
    private readonly Func<T, bool> continuePredicate;
    private readonly Exception exitException;

    // looper core method
    private IPromise<T> Loop(T previousOutcome)
    {
        return continuePredicate(previousOutcome)
            ? loopingBody().Then(Loop)
            : Promise<T>.Rejected(exitException);
    }

    // constructor
    public PromiseLooper(Func<IPromise<T>> loopingBody,
        Func<T, bool> continuePredicate)
    {
        this.loopingBody = loopingBody;
        this.continuePredicate = continuePredicate;
        // setting up a exit exception
        this.exitException = new Exception("LooperExit");
    }

    // looping starts when this called
    public IPromise StartLooping(T initialOutcome)
    {
        var loopPromise = new Promise();
        // reporting back loop status as a promise
        Loop(initialOutcome).Catch(e =>
        {
            if (e == exitException) loopPromise.Resolve();
            else loopPromise.Reject(e);
        });
        return loopPromise;
    }
}

用法示例:

// where you create the looper and call StartLooping()
private void Start()
{
    var looper = new PromiseLooper<int>(GenerateRandomInt, IsNotFive);
    looper.StartLooping(0).Then(
        () => Console.WriteLine("Loop complete!"),
        e => Console.WriteLine($"Loop error! {e}"));
}

// the predicate that decides the end of the loop
private bool IsNotFive(int x)
{
    return x != 5;
}

// the method you want to loop!
private IPromise<int> GenerateRandomInt()
{
    var promise = new Promise<int>();
    //this could be any async time consuming call
    // that resolves the promise with the outcome
    LeanTween.delayedCall(1,
        () => promise.Resolve(Random.Range(0, 10))
    );
    return promise;
}