8 ms·
Transpiled For-Of Loops Are Bad for the Client
- based2 9y agohttps://lobste.rs/s/wwgnlh/transpiled_for_loops_are_bad_for_client https://lobste.rs/s/wwgnlh/transpiled_for_loops_are_bad_for_...
- draw_down 9y agoPolyfill
- javan 9y agoSee also: https://twitter.com/javan/status/907949203325964288 https://twitter.com/javan/status/907949203325964288
- fiddlerwoaroof 9y agoThe other way to deal with this particular problem is to do: [].forEach.call(myNodeList, myCallback)
- vkjv 9y agoGood tip, but while it works in this specific case, it isn't generally equivalent. "for of" works on anything that is iterable. This will only work on array-like objects.
- deleted 9y ago[deleted]
- Klathmon 9y agoAnd the major reason I use for-of is because you can `await` within a loop easily.
- ryanmarsh 9y agoI get it. It’s roughly 5 times the lines of code and there are better polyfills for IE (sounds like an easy bug to fix in Babel) but what’s the actual performance impact when it runs?
- zengid 9y agoIt looks like Typescript also transpiles to a similar try..catch..finally pattern: https://blog.mariusschulz.com/2017/06/30/typescript-2-3-downlevel-iteration-for-es3-es5#the-downleveliteration-flag https://blog.mariusschulz.com/2017/06/30/typescript-2-3-down...
- ChrisSD 9y agoAre you sure? https://www.typescriptlang.org/play/#src=const%20inputs%20%3D%20document.querySelectorAll('input%2C%20select%2C%20textarea')%3B%0D%0Afor%20(let%20input%20of%20inputs)%20%7B%0D%0A%20%20%20%20console.log(input)%3B%0D%0A%7D%0D%0A https://www.typescriptlang.org/play/#src=const%20inputs%20%3...
- zengid 9y ago..with the `--downlevelIteration` flag set and ES5 or ES3 set as the compilation target. I should have clarified. From the article: "TypeScript 2.3 introduced a new `--downlevelIteration` flag that adds full support for the ES2015 iteration protocol for ES3 and ES5 targets. `for...of`-loops can now be downlevel-compiled with correct semantics."
- jasonthevillain 9y agoI've learned the hard way to check Babel's output for sanity. Sometimes its fine. Other times it's madness or pulls in a massive polyfill. That's sort of inherent to making a transpiler: they need to match the spec. I usually just need one specific behavior that could be written in a 3 line helper function.
- Klathmon 9y agoThey have a "loose" mode for most compilations that can benefit from them. In loose mode they might not follow the letter of the spec, but can cheat for speed or size.
- hzoo 9y agoYep you got that right! Only thing I can think of (since changing the default to be non-spec would be odd) is just warning on larger code size outputs and saying you could try loose mode in certain plugins/situations. Lastly would be do start creating a tool that does runtime checks to determine how small the compiled output should be
- TazeTSchnitzel 9y agoWhy would a polyfill of Symbol make this work? If a browser doesn't support Symbol, it won't have a property named [Symbol.iterator] on DOMNodeList, right?
- lexicality 9y agoBecause the 259kb polyfill also adds iterator support to everything.
- good-econ1351 9y agoMy polyfill to support back to ie11 takes less than 100kb, raw. If you're polyfill is 259k, you're probably doing something suboptimal
- 35bge57dtjku 9y ago> but I sometimes wonder if we’re living in a collective delusion that the current toolchain is great when it’s really just morbidly complex. Epiphany of the year there.
- TAForObvReasons 9y ago> I’m not sure this is an issue with Babel per-se, but maybe a more meta topic of the cost of polyfilling. The overarching problem is that the web platform itself is so fragmented, with different levels of support for various features, that polyfilling and transpilation are important. Strictly speaking, if every relevant browser supported for..of natively, there should be no need to transpile. You could avoid the whole for..of problem by writing ES5 JS code, but for many people that's apparently a worse option than dealing with Babel.
- rhapsodic 9y ago> You could avoid the whole for..of problem by writing ES5 JS code, but for many people that's apparently a worse option than dealing with Babel. Yes, and I don't understand that at all. I don't have any problems writing plain old ES5 code.
- noir_lord 9y agoYou don't, I do. TypeScript has been a revolution in the way that I write client side code, been able to write in a superset of a language I already know against existing code in a way that I and my tools can reason about and with faith (backed up by experience) that I can write it once and know it will work basically everywhere is huge. Is it a perfect tool? by no means, it's output messages can be a bit opaque. That I can also use features now that won't make it into browsers everywhere for a while and that they'll gradually replace those features as browser support for them increases, ES6 was a good step in the right direction (and you can target it from TS and in many cases there aren't that many differences between clean ES6 and TypeScript's output but I can also set the target to ES5 and use it). FWIW: https://github.com/Microsoft/TypeScript/pull/12346 https://github.com/Microsoft/TypeScript/pull/12346 https://basarat.gitbooks.io/typescript/content/docs/types/lib.d.ts.html#lib-option https://basarat.gitbooks.io/typescript/content/docs/types/li...
- kozak 9y agoI also noticed that Babel's transpiled ES6 classes suffer from abstraction leaks quite a lot. This is especially bad if you have two classes in the same class hierarchy, where one class resides in a babelified module, and another one in a native module.
- loftsy 9y agoTry out dart or typescript.
- spraak 9y agoBut those just transpile to JS as well and likely suffer the same problems.
- carussell 9y agoGo one step further, eliminate the polyfill, and just write a traditional for loop. Several years ago, I decided to try out the then-newish E4X for-each syntax. It was only implemented in SpiderMonkey, but that was okay because not only am I not a web developer, this project wasn't meant to run on the web, either—it just happened to be in JS. I knew for a fact that this code wouldn't be running anywhere except SpiderMonkey, so I dug in. After some research, though, I discovered some problem. Some subtlety that made it a non-starter. So I kept writing traditional for loops. A couple years later, I was starting work on a kind-of-new codebase; it was essentially greenfield, but I knew I was going to end up cribbing a bunch from an existing codebase. When my fingers began typing out the familiar `for (`, I paused. This would be the first for loop introduced in the project, and I should maybe make a decision to get things right from the beginning. By now, for-of had arrived, and it was being standardized in ESx proper. Should I maybe do another survey of the lands and look into giving the new iteration syntax a shot? It was a simple iteration: a straight pass through a list of items, one after another. And in addition to being able to address items by index, they all had the equivalent of a "next" pointer for the item that followed. So I deferred; I went with a while loop instead, chasing "next" pointers to avoid having to do some in-depth research on the then-current state of `for`. I got pretty far doing this, actually, and was able to go the next few days writing all my loops in this way, just to avoid tacitly endorsing either for-of or traditional for loops. After several days, however, that ran out, and I came to point where I would not be able to sidestep it and needed to make a decision. So I stopped, and dug in again. Another problem surfaced: for-of was only implemented in the newest releases of SpiderMonkey, and the only enhanced iteration syntax available in previous releases that this code would need to run on was the now-deprecated for-each. I thought really hard about this. Was there some part of the language that could be exploited to find some clever way around this? No. Maybe consider a transpiler? After all, for-of would be standard, so eventually support is going to be widespread enough that even the lowest common denominator will have native support... But then I stopped. Because the whole thing is stupid. Just write a damn for loop. What was the real problem? Trying to save time? Except how much time was already sunk into trying to shave off, what? Like one second of typing? Maybe two? And the tradeoff for that development time gain is (a) a runtime performance penalty; (b) having to either throw everything older than version x by the wayside or spend all those extra seconds and more to put some transpiler crud in place; and (c) code that's harder to understand for anyone who doesn't fetishize staying on top of the newest language features. The latter also involves people who typically work in other languages and would probably immediately understand `i = 0; i < n; i++` (even if it's not idiomatic for their preferred ecosystem), but likely wouldn't immediately grasp the nuances of the new iteration protocol. What's wrong with `i = 0; i < n; i++`, anyway? It's not even that verbose. What it is, is predictable. Which means your eyes get used to it, just like any other pattern. Except traditional for loops are powerful, too, because you might want to do something, say, for every fourth item. Or maybe skip the first. Or iterate backwards. And you can. In each case, anything not fitting the traditional pattern will stick out. Anyway, I still write traditional for loops today. I don't feel like I'm missing out on anything, and my thoughts very much echo the sentiments in an essay published a few years ago on a similar topic: "Java for Everything". https://www.teamten.com/lawrence/writings/java-for-everything.html https://www.teamten.com/lawrence/writings/java-for-everythin...
- jrochkind1 9y agoSometimes I think that it might actually be fine to just stick to JQuery and ES5 after all.
- wruza 9y agoDeveloper: Non-moving standards, motherfucker, do you speak it? Web: YES! Developer: Describe what well-established platform looks like! Web: W-what?
- alangpierce 9y ago> First stop, I looked at babel-polyfill.js but was surprised to see it is 259kb unminified/uncompressed. I’m sure it does a lot, but the entire JavaScript bundle for the whole site is only 93kb unminified/uncompressed. It’s hard for me to recommed a +300% increase. I just tested it, and the minified and compressed babel-polyfill is 34K. Going from 12K of JS to 46K of JS just to make modern loops work may be a little unsettling, but those are both really small numbers these days. The blog post is about 10x bigger. It's also worth noting that babel-polyfill adds lots of other useful stuff; there are plenty of similar problems that you'll never have to think about if you're willing to drop in the whole babel-polyfill. I think that for most use cases, bundle size is a distraction and it's best to just focus on building things. You should still keep an eye on bundle size in case it gets out of hand, but I don't think that's the case here.
- jopsen 9y agoAgreed, you can always optimize for size later... And conditionally load the polyfill, etc. All that said: dropping all the fancy logic and just going with plain JS has some nice simplicity to it. In the JS world you can spend a lot of time on tooling.
- always_good 9y agoI agreed with you until I had to be wary of performance on mobile devices where the user may have to pay 10s of milliseconds just on boot per every 15kb of javascript you add. At the end of the day you ask yourself "do I really need to add 34kb for 'for..of' or can I get away with a small concession?" like the blog post asked. You may find that it might not cost you much to pay attention to your trade-offs.
- bluepnume 9y agoYou can set babel to `loose` mode to avoid having the transpiled output use `Symbol`. What I would love though is a "super-loose" mode which takes for-of and transpiles it directly to for-in -- i.e. excluding any support for iterators and just functioning on indexable collections with a defined `.length`. That would be enough to get concise output in most cases if you're confident you're always iterating over static collections.
- always_good 9y agofor..of works with any iterator which is essentially the interface { next() }, thus arbitrary. If you limit it to only indexable things, then I'm not sure why you would make it a point to use for..of.
- hzoo 9y agoI answered this on twitter: https://twitter.com/left_pad/status/936721918840983554 https://twitter.com/left_pad/status/936721918840983554 And added this option + optimization for v7 https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-for-of#assumearray https://github.com/babel/babel/tree/master/packages/babel-pl... so can look forward to that now =D.
- philmander 9y agoOne upon a time web developers had to wait for browsers to support new features. Using Babel does not automatically mean that this no longer true.
- larzang 9y agoThe problem is that IE11 is not eol until the end of 2020, and management can be loathe to drop support for it while it still makes a blip in the traffic, even a tiny one. It's one thing to wait for the actually supported browsers to all catch up on a new feature, but IE11 is abandonware that can't be abandoned, and the situation just keeps getting worse.
- msiggy 9y agoYou know its a good language when you have this much trouble writing a simple for loop.
- always_good 9y agoYou're confusing the myriad of client runtime environments with the language. Whining about the former is to just whine about history, like how 'referer' is misspelled.
- cromwellian 9y agoWhat is iterator.return()? That seems non-standard?
- hajile 9y agoThe transpile issue on mobile is greatly overstated. The vast majority of mobile browsers (by market share) have very good es6 support. Instead of one version, websites need one with minimal transpiling and one with full translation for older browsers. Sure you need a little more setup, but it's not only better for clients, but it's also much better for development because your source maps, breakpoints, and actual variables are much closer to what the browser is actually executing (this is especially important for generators because they compile to case statements that aren't easy to debug).
- DiThi 9y agoFor loops are the single biggest reason we still use Coffee Script. They're the most efficient and most compatible of the alternatives. The syntax of the loops is very clean and they all compile to simple "for( ; ; )" or "for( in )" loops. And a few months ago that we considered moving to something else, Coffee Script 2 was released with all that was missing for us.
- underwater 9y agoThe author has huge gaps in his understanding the language works, and these tools work, and he seems to have zero interest in correcting that. It sucks that he had a bad experience. And, yes, it's a lot to understand. But he blames Babel, the authors of polyfill code, and Twitter without putting any attempt into filling the gaps in his understanding. The Babel homepage literally says what it does. > These plugins allow you to use new syntax, right now without waiting for browser support. When a Twitter user tried to help him by asking how a syntax transform could polyfill `forEach` on a `NodeList` -- something that would be impossible in JS -- he was completely dismissive. > Science? ️ I dunno. Maybe if the object was previously queryselected? [...] I'm way past that problem now. Polyfills can be heavy because they have to be forward compatible and implement behavior to spec. If they don't then you end up shipping code that breaks when browsers are updates and the polyfill becomes redundant. That's how you break the web.