5 ms·
How many times do I have to say this. Generator/Yields and equivalent constructs don't actually solve the callback problem, they exacerbate it. That is, because
by pyalot2 13y ago
How many times do I have to say this. Generator/Yields and equivalent constructs don't actually solve the callback problem, they exacerbate it. That is, because you only get to direct one call-level down, but, that's now how you'd code. You'd have some perfectly synchronous code until it hits some infrastructure I/O part some N call layers down, and then now what? You're gonna convert everything to yields? Got any idea how messy that is?
Javascript needs proper co-routines. Nothing short of co-routines will solve the issue. Please lookup microthreads, greenlets, fibres, etc.
- jeswin 13y agoI think you have it wrong, generators would solve the problem. If you have asynchronous code N call layers down, then everything above it would be asynchronous as well. So you would only need to yield the first call. Check how Q.nfcall works.
- pyalot2 13y agoThat's a faulty assertion. A generators working principle is that it can resume into the closure if the "caller" is looping over the generator. Let's assume a simple call stack, A -> B -> C. Now C would be a generator, so B has to loop over C instead of just calling C. But what about A? Well if B is now looping, and if yielding out of a loop is how you cooperatively multitask, then A now has to loop over B. So you have to convert the entire call stack from A -> B -> C to A(for ... in B(for ... in C)). You can argue that a compiler can make this invisible, however there are some serious drawbacks with that in that: 1) One solution would be to convert any call into a loop. How do you know which ones are actual calls (stuff like Math.pow etc?) 2) You only convert things that have generators in their call stack to loops, but how do you know that? Both the above scenarios would depend on being able to infer the "generatoricity" of the called symbol. Something which is very, very hard to do for dynamic languages like Javascript. Co-routines work differently. Co-routines save the entire call stack and restore another one on switching to somewhere different. Hence if you have A -> B -> C and C is some I/O that would have to go to some scheduler, then the A -> B -> C call stack is saved, the co-routine would switch to say, Main -> Scheduler, the scheduler would do its work, and when it's ready, it switches back to A -> B -> C where that co-routine left of.
- jeswin 13y agoI am not sure if I followed entirely. But let me give you an example, again with Q.nfcall. MultiplyAndSquare = (a, b, cb) -> Multiply a, b, (err, product) -> Square product, (err, square) -> cb null, square #ignore errors for now. nonCallbackVersion = (a, b) -> Q.nfcall(MultiplyAndSquare, a, b) result = yield nonCallbackVersion(10, 20)
- pyalot2 13y agoYou see how the caller has to be aware of how what he's calling is a generator. You're dragging generators all the way trough the call chain as soon as anything deep down in the call chain is a generator, and you'd want to use generators to acomplish asynchronicity. Let's say you write some code A, which uses some other code B, which uses some other code C. C is also used by various other pieces of code like X, Y, Z, D, E, F, G, H, J, K etc. Now at some point you decide, well, you don't like callbacks in C, so you'll convert it to something asynchronous. You'll now just have to refactor A, B, D, E, F, G, H, J, K, X, Y and Z to make all their calls to C a generator iteration, and you'll have to convert any code that depends on A, B, D, E, F, G, H, J, K, X, Y or Z to be aware of that, any any code which uses those, to be aware of that.
- jeswin 13y agoSure, call sites of C will need to change, since C's signature changed. That's expected, since C doesn't even take a callback anymore. But callers of A,B,D,E etc don't need to change. A,B,D,E etc can still take a callback parameter and return C's result via the callback. MultiplyAndSquare = (a, b, cb) -> Multiply a, b, (err, product) -> Square product, (err, square) -> cb(null, square) #ignore errors for now. C = (a, b, cb) -> MultiplyAndSquare(a, b, cb) ABDEXY = (a, b, cb) -> #Calls C C(a, b, cb) callersOfABDEXY = (a, b) -> #Calls ABDEXY ABDEXY a, b, (err, result) -> Console.WriteLine(result) #Refactored C. Returns a promise instead. refactoredC = (a, b) -> (Q.async -> result = yield Q.nfcall(MultiplyAndSquare, a, b) return result)() #Refactored ABDEXY. refactoredABDEXY = (a, b, cb) -> #Change needed here, since C now returns a promise refactoredC(a, b).then (result) -> cb(null, result) #No need to refactor callersOfABDEXY
- nonchalance 13y agoexacerbate is the correct spelling