Open pschiffmann opened 5 months ago
Sounds like the Dart zones feature is pretty equivalent/analogous in functionality; am I understanding the code correctly? I wonder how they've resolved some of the issues we're currently looking into about the context where various callbacks run in.
The old js zones proposal was basically the same thing, and is already mentioned in prior arts.
In Dart, you don't have to create a Variable, you could just as well use any other value as a zone key. You can still have private variables, by using a module private object (i.e. the equivalent of const asyncVar = Symbol()) as key. But you can also use other objects as zone keys, which might be handy in certain situations.
While not the only objection to Zones, the mere ability to do this was one of the major objections. AsyncContext.Variable
was very specifically proposed to avoid sharing any objects unless you have access to them.
You can set multiple zone keys at once. For example if you have two variables asyncVar1 and asyncVar2 and you want to set values for both of them, with the current proposal, you'd have to write it like this, right? asyncVar1.run("A", () => asyncVar2.run("B", () => ...)). With the Dart zone API, you can do a single runZoned(..., { asyncVar1: "A", asyncVar2: "B" }) instead.
What is a use case here? The main use cases this proposal has been designed for (schedulers, tracing, loggers) all only need a single variable.
Like it would not be that hard to spec a AsyncContext.runAll([[asyncVar1, val1], [asyncVar2, val2]], cb)
or similar, but withou motivating use cases what is the point?
I wonder how they've resolved some of the issues we're currently looking into about the context where various callbacks run in.
Dart zones are not implemented in user land, they are deeply integrated into the Dart VM and (probably?) built into the VM scheduler in C++.
The old js zones proposal was basically the same thing, and is already mentioned in prior arts.
Dart zones were actually shipped and can be used. I don't know if that's useful/relevant for the spec process. Based on the API similarity, Domenic was probably aware of the Dart implementation when he wrote his proposal, but I thought I'd mention it in case it's new for someone else in here.
You can set multiple zone keys at once.
What is a use case here? The main use cases this proposal has been designed for (schedulers, tracing, loggers) all only need a single variable.
Frameworks like Angular and NestJS support dependency injection through an annotation based system. But that requires a compile step (at least for now). And you can only annotate classes, not functions; so you have to wrap all your logic in Service classes.
Here is an example. Let's say you want to store a user uploaded profile picture file in AWS S3. With current NestJS, you'd have to define a service class to utilize their DI mechanism:
@Injectable()
class ProfilePictureUploadService {
constructor(private awsService: AwsService, private logger: Logger) {}
async uploadProfilePicture(user: User, file: File) {
const uploadPath = `user-uploads/${user.id}/${file.name}`;
try {
await this.awsService.uploadFile(uploadPath, file);
} catch(e) {
this.logger.error(e);
}
}
}
Using AsyncContext.Variable
, you can just write a simple function. You don't need the class boilerplate anymore.
async function uploadProfilePicture(user: User, file: File) {
const awsService = awsServiceVar.get();
const logger = loggerVar.get();
try {
await awsService.uploadFile(uploadPath, file);
} catch(e) {
logger.error(e);
}
}
Complex NestJS and Angular apps can have dozens of service classes. AsyncContext.runAll([[asyncVar1, val1], [asyncVar2, val2]], cb)
would be quite useful to provide all of them to my application.
In Dart, you don't have to create a Variable, you could just as well use any other value as a zone key.
While not the only objection to Zones, the mere ability to do this was one of the major objections.
AsyncContext.Variable
was very specifically proposed to avoid sharing any objects unless you have access to them.
Thinking about it again, the proposal approach makes more sense. I was thinking about a hypothetical use case where I might want to use a runtime value as a key. But if I really need to do that, I can just do:
const wm = new WeakMap();
wm.set(myRuntimeValue, new AsyncContext.Variable());
I agree that the AsyncContext.Variable
API is more convenient than the zone API 99.9% of the time. Now that I realized it's also equally powerful, I do prefer the proposal API too. :)
While not the only objection to Zones, the mere ability to do this was one of the major objections.
AsyncContext.Variable
was very specifically proposed to avoid sharing any objects unless you have access to them.
Agreed. We cannot give access to other Variable
s, but nothing is stopping a user from using an object with many keys in their own Variable
. Just don't mutate it, you'll have a bad time.
AsyncContext.runAll([[asyncVar1, val1], [asyncVar2, val2]], cb)
would be quite useful to provide all of them to my application.
https://github.com/tc39/proposal-async-context/issues/60 is related. While we may not do a Disposable
, I wouldn't be opposed to a runAll(…)
that sets multiple variables at once.
In Dart, you can use zones to achieve the same goal as you're going for with this proposal. Dart zones are not a 3rd party library, but are provided by the runtime - that means they work with
async/await
. I haven't seen them mentioned in the "prior arts" section, so I thought I'd mention them here. If you were aware of them already, kindly close this issue. :-)Here is how you can translate the AsyncContext.Variable example to the Dart zone API:
My two cents on the API: I personally prefer the Dart zone API design over the proposal for two reasons:
Variable
, you could just as well use any other value as a zone key. You can still have private variables, by using a module private object (i.e. the equivalent ofconst asyncVar = Symbol()
) as key. But you can also use other objects as zone keys, which might be handy in certain situations.asyncVar1
andasyncVar2
and you want to set values for both of them, with the current proposal, you'd have to write it like this, right?asyncVar1.run("A", () => asyncVar2.run("B", () => ...))
. With the Dart zone API, you can do a singlerunZoned(..., { asyncVar1: "A", asyncVar2: "B" })
instead.