我正在将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
类的答案。
答案 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;
}