Comments (5)
Enumerables usually act as enumerator-generating factories, so they can be re-used for multiple iterations. This is problematic for us, because we have no knowledge of when the user is finished with the enumerable, so we can't use object pooling conveniently. We could add a Dispose
call to re-pool it, but enumerables are not usually used that way, and that adds extra boilerplate code.
I'm thinking to solve this issue, just make PromiseEnumerable
single-use, so when GetAsyncEnumerator
is called, it is invalidated and cannot be legally called a second time. This matches how enumerables are usually consumed. It is unusual (though not rare) to consume an enumerable more than once. If an enumerator needs to be consumed multiple times, then just call the same function again that built it the first time. This makes it so we only need 1 allocation, and it can be re-pooled when the iteration is complete.
Because "just call the same function again" is not quite the same thing as directly consuming the same enumerable multiple times, due to side effects, I'm also contemplating adding Preserve
and Forget
APIs (to match the Promise
APIs) to allow consuming the same enumerable more than once. But I'm not 100% on that, because it complicates nested enumerables scenarios (when 1 is preserved, it will need to preserve its child). I think it's best to just do the single-use only, and revisit preserve/forget in the future if it's really needed.
from protopromise.
Or another option is to only implement PromiseEnumerator
which is single-use by contract, and don't implement PromiseEnumerable
at all (PromiseEnumerator.GetAsyncEnumerator
would just return itself to still work with await foreach
syntax).
I think that's a better option. And then revisit PromiseEnumerable
in the future if it makes sense.
from protopromise.
After some more investigation, I've found that I cannot safely implement the DisposeAsync
pattern without compiler support.
Consider this implementation:
static PromiseEnumerable<int> IterateAsync()
{
return PromiseEnumerable.Create<int>(async (writer, _) =>
{
try
{
await writer.YieldAsync(0);
await writer.YieldAsync(1);
}
finally
{
Console.WriteLine("Finally block");
}
});
}
Then consumed with a break
:
await foreach (var elem in IterateAsync())
{
Console.WriteLine("elem: " + elem);
break;
}
We have no way of safely running that finally block while skipping over the rest of the code. We could try to reject or cancel the promise returned by writer.YieldAsync
, but that's not safe, because the user could catch it. Even if we try to use a special exception, it could still be caught by a general catch clause.
from protopromise.
I realized that C# prevents usage of yield
inside a try
block that has an associated catch
block. https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1626
If we extend that restriction to this, it alleviates the issue in my previous comment. The only thing left to make this feasible then is to investigate whether a Roslyn analyzer can be added to prevent using await writer.YieldAsync
in such a try/catch block.
from protopromise.
I was able to prototype an analyzer that successfully shows an error if it's used incorrectly. Unfortunately, Unity doesn't have a way to include analyzers in packages that just work out-of-the-box, so I guess the only course of action is to create an analyzer nuget package, and recommend users to install it separately.
from protopromise.
Related Issues (20)
- Methods to troubleshoot UnobservedPromiseException HOT 24
- Fatal error when building macOS player HOT 3
- `PromiseYielder.WaitOneFrame().ToPromise()` waits an extra frame.
- Add analyzer to prevent accidental misuse of `AsyncEnumerable`
- Add `Promise.ParallelForEachAsync`
- Add async Linq extensions for `AsyncEnumerable` HOT 1
- Add `AsyncEnumerable.Merge`
- Add `AsyncEnumerable` convenient generators
- Decouple Progress from Promises HOT 1
- Add `Promise.Finally` overloads that accept `Func<Promise>` delegates
- Add overloads that accept `Span<T>` parameter
- Update readme with instructions to install analyzer in Unity
- Add `Channel` type
- Add parallel async Linq
- Add Unity `Awaitable` conversion to `Promise`
- Add Promise groups
- the README and CHANGELOG for 3.0 have the wrong year HOT 1
- Add `Promise.Each` to process operations as they complete
- Add `Promise.GetRetainer()` API
- Compilation error if to use it together with plugin for "oidc-client" (needed for Azure auth) HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from protopromise.