-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Going Super-Lazy... #8701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This may not be necessary for everything but if you can imagine the chain of dependencies that may be avoided if a particular class is never instantiated even though it's imported. |
So here's my real world example: The problem is, the LINQ library is _huge_. And although I would love for every class that implements So my plan is/was to create an asEnumerable():IPromise<ILinq> {
return require("../System.Linq/Linq")
.then(Enumerable=>Enumerable.from(this));
} Although the above fits what I'm trying to accomplish, it's basically clunky and not ideal. var names = myIEnumerable.linq // where .linq is accesor that returns a promise that may have already resolved.
.select(person=>person.name); Is this making sense? |
I do not think TS should be hiding that much details from the user. Moreover, most of the value outlined above can be achieved by structuring your project into units ( be it 1 class, multiple classes,..) and having every unit in a module. One other thing, this would break single file compilation, as the compiler needs to know were to get the class from in the first place, which relies on global program information. |
related discussion in #2508 |
Isn't SystemJS already doing this? |
@electricessence I'm pretty sure the ES module loader spec will cover your async module loading case. Here is some related issues I filed before: |
closing as out of scope of the TS project at the time being. |
@mhegazy ...
Would not break it. You would still use the same import statements. The code would read exactly as you would normally write synchronous code. But the |
So again, there is a way to manually do this. It's just the nature of TS and it's ability of async/await rewriting that allows fro this to happen. I still hope people think about it or how to enable it. |
As far as I know, there is no loader that does this. The code would have to be interpreted/re-compiled to allow for pure async loading. |
there is no way to do this correctly unless you 1. do global analysis and know which classes are created asynchronously and which are not, or 2. rewrite every construct call to an asynchronous operation. and in both cases, the cost of rewriting a all your new's to async, and then transforming the rest of your code is substantial. I do not think there is much value here to be frank to do this all the time for all classes and all instantiations. you are much better off identifying what are units of code that should live together, and importing them when needed. then use a bundler (or just |
So the obvious point is that if you are bundling or 'outFile' then this is a useless concept for you. Here's my example...Let's say you have an interface like Now let's say you have a method called export function from(e:any):IEnumerator<T> { /* complex logic to select the right class */ } At the head of this file is a huge number of imports for different enumerators. Bundling doesn't solve this issue either. It just incurs the unnecessary cost up front. |
@mhegazy and sorry, just to clarify... I don't see what I'm suggesting as anything different to what the async/await rewrite does. Simply stated, your imports declare which potential classes you might use, and in place of their constructor, you have some async function. It would not require global analysis. |
But this is based on what is reasonably expected to be agreed as a standard for the ECMAScript language. The place to start for this would be TC39 or more likely the WHATWG for the System Loader. |
For this case you can isolate enumeration in one module, then use conditional loading(require, or System.import, ...) and await the promise. |
Let me try and explain how you would need to do this effectively today (which isn't too bad if it's just one thing) and it will be obvious then how async/await helps. And what I'm suggesting is simply allowing a rewrite (maybe I need to get my hands dirty in the TSC) that replaces Also note, I understand that this is not a concern for most. It only really applies to browser/web loading and not NodeJS/CommonJS. But if I was to create a UMD module I would want this. If I need to do what I'm talking about today and still retain type information...
// IMyHugeUtility.d.ts
export interface IMyHugeUtility{
/// ... endless methods inside ...
}
myMethod():Promise<IMyHugeUtility> {
var r = Promise.pending(); // Because require (AMD) doesn't inherently return promises :/
require("./path/MyHugeUtility",(MyHugeUtility:IMyHugeUtility)=>{
r.resolve(new IMyHugeUtility());
});
return r;
} And subsequently... Putting async in front of this method then makes it nice to consume in a synchronous way. |
What you are talking about is supporting one very specific pattern at the cost of increasing the complexity of the compiler, adding compiler behaviour that isn't based on any existing or planned ES language feature and could easily "surprise" many developers, all for the cost of not being explicit about dependencies. This seems like a case of the fallacy of composition. How would you do with namespace resolution, where there are conflicting names? What if someone has a class name that conflicts with a built in (e.g. Bluebird's Promise)? |
@kitsonk I agree that this does seem like an edge case. And in no way am I assuming it's easy. "Namespace resolution": How is this even an issue? You would still write your code as normal including your imports. import MyHugeClass from './MyHugeClass';
import {myFunc} from './aLotOfCode';
import {somethingThatRunsImmediately} from './overHere';
/* The bulk of your code. */
somethingThatRunsImmediately();
// Not sure how much of the async portion of this pattern would be required.
// (just one example here)
async function somethingThatMayNeverBeCalledFor():Promise<MyClass>
{
var n = new MyClass();
myFunc(n);
return n;
}
/* some more code */ So the compiler would treat the TS as the same code with resolution as normal. So the resultant (pseudo) JS code would be: // Note: no actual imports here.
define(["require", "exports", "somethingThatRunsImmediately"],
function(somethingThatRunsImmediately)
{
/* The bulk of your code. */
somethingThatRunsImmediately();
function somethingThatMayNeverBeCalledFor()
{
return new Promise(resolve=>
{
require("./MyClass", MyClass=>
{
var n = MyClass();
// same pattern for myFunc
resolve(n);
});
});
}
/* some more code */
});
}); Sorry if that seems either repetitive or long winded. What would be a good first step if I wanted to contribute to TypeScript and potentially implement this feature? |
As noted earlier this feature is out of scope of the TS project at the time being. |
@mhegazy noted |
@kitsonk (and everyone else). Thanks for the discussion. This actually helped me move forward with a CommonJS solution. Still not perfect, and I still need to import a huge number of declarations, but it will work well enough for now. |
So I was thinking last night about how async/await is coming to ES5 via TS2.0. I'm super excited for this. But then it dawned on me... Putting aside code that doesn't run in the browser. Why can't we have a TS compiler that has a
"superLazy":true
mode?What is Super-Lazy?
So you can write code for JS that takes dependency management a step further. Instead of simply:
could potentially write it:
which either would really equal:
Instead of forcing the developer to write more async code, the TS compiler could add the code that uses the async/await pattern automatically.
Why?
Because we build classes and libraries that link to each other but sometimes in our lazy world of code that particular dependency never gets used! So why load the dependencies ahead of time? Why not defer them to the last second just like an
AsyncLazy<T>
(akaPromise<T>
) class or something?The text was updated successfully, but these errors were encountered: