2 ms·
The main benefit of coroutines is avoiding inversion of control flow. A classic example is iterators. Its very natural to write an iterator like for(i=0;
by ufo 13y ago
The main benefit of coroutines is avoiding inversion of control flow.
A classic example is iterators. Its very natural to write an iterator like
for(i=0; i<10; i++){
yield i
}
But to program the same thing without coroutines you need to keep all the control flow state explicitly and write the logic inside out like a state machine:
i = 0
int getNext(){
if(i >= 10){
return Done
}else{
temp = i
i = i + 1
return temp
}
}
This inversion of control gets worse the more complicated your control flow is. For example if you have a recursive structure
iterate_tree(t){
if(t.isLeaf){
yield t.value
}else{
iterate_tree(t.left)
yield t.key
iterate_tree(t.right)
}
}
then you suddenly need to also keep a bunch of stacks as your control flow state instead of just a single integer. I wont even try to write it because its just that ugly!
---------
One way to prevent the inversion of control is to use internal iterators, where you pass a code block to the iterator:
void iterate(callback){
for(i=0; i<10; i++){
callback(i)
}
}
This works just fine sometimes but there are limitations: you cant't break out of the iteration at the middle and you cant interweave to iterations in parallel. Another situation where you see this kind of limitation is Javascript. Using callbacks to do async IO is better than writing everything inside out with all state in global variables (like you would do in C) but its a PITA to do simple things like chaining async calls one after the other inside a loop For example, with coroutines you could do something looking like
foreach(url in list){
var x = await getUrl(url)
console.log(x)
}
restOfTheCode();
while in regular JS you need to use recursive functions instead:
function go(i){
if(i >= urls.length){
restOfTheCode();
}else{
getUrl(function(x){
print(x)
go(i+1)
})
}
}