2015-04-17 15 views
5

Eksperymentuję z generatorami ES6 z pomocą babel i nie mam pojęcia jak (lub jeśli!) Potrafię efektywnie wykorzystywać funkcję asynchroniczną opartą na oddzwanianiu do wyprowadzania iteratora.Generatory ES6: transformacja wywołań zwrotnych do iteratorów

Załóżmy, że chcę napisać funkcję, która pobiera wiele adresów URL, asynchronicznie pobrać je i odsyłać, gdy tylko zostaną pobrane. Chciałbym móc napisać coś jak następuje:

let urls = ['http://www.google.com', 'http://www.stackoverflow.com' ]; 
for ({url, data} of downloadUrls(urls)) { 
    console.log("Content of url", url, "is"); 
    console.log(data); 
} 

Jak mogę wdrożyć downloadUrls? Idealnie chciałabym móc napisać następujące:

var downloadUrls = function*(urls) { 
    for(let url of urls) { 
     $.ajax(url).done(function(data) { 
      yield data; 
     }); 
    } 
}; 

To oczywiście nie zadziała, ponieważ `` wydajnością „” jest wywoływana wewnątrz wywołania zwrotnego, a nie bezpośrednio wewnątrz generatora. Mogę znaleźć wiele przykładów online osób próbujących tego samego, są one albo not much transparent), wymagają enabling browser/node flags, albo używają specyficznych dla węzła funkcji/bibliotek. Biblioteką najbliższą temu, czego potrzebuję, wydaje się być task.js, ale nie mogę mieć nawet najprostszego przykładu uruchomionego w bieżącej przeglądarce Chrome.

Czy istnieje sposób na uzyskanie zamierzonego zachowania przy użyciu standardowych i aktualnych funkcji (Z obecnym mam na myśli używanie transpilatorów takich jak babel, ale bez potrzeby włączania dodatkowych flag w przeglądarce) lub czy muszę poczekać na async/await ?

+0

Jak dokładnie te internetowe wyjaśnienia nie są dla ciebie "przezroczyste"?Artykuł Davida walsha jest jednym z najlepszych, jakie przeczytałem (ale musisz przeczytać całą serię oczywiście). – Bergi

+0

Wydaje się to istotne: Propozycja generatora asynchronicznego https://github.com/jhusain/asyncgenerator (jeszcze nie jest obsługiwana AFAIK). –

Odpowiedz

1

Czy istnieje sposób, aby uzyskać zamierzone zachowanie przy użyciu standardowych i aktualnych możliwości

Tak, użyj obietnic i generatorów. Wiele obiecujących bibliotek, a niektóre samodzielne, wyposażone są w generatory "coroutines".

Ale zauważ, że you cannot mix iteration with asynchrony, możesz używać generatorów tylko dla jednego. Twój przykład wydaje się nieco mylić - wygląda na to, że oczekujesz, że pętla for ({url, data} of downloadUrls(urls)) { zadziała synchronicznie, co nie działa.

Czy muszę czekać na async/await?

Nie, nie musisz czekać, Babel already supports them!

1

Oto czysty sposób użyć Generator/iterator spłaszczyć asynchroniczny kod, który działa na mnie w node.js:

var asyncProcedureGenerator1 = function*() { 
    var it = yield(0); //get a reference to the iterator 
    try { 
     var a = yield (asyncPart1.bind(it))(0); //call the function, set this = it 
     var b = yield (asyncPart2.bind(it))(a); 
     var c = yield (asyncPart3.bind(it))(b); 
     console.log("c = ", c); 
    } 
    catch(err) 
    { 
     console.log("Something went wrong: ", err); 
    } 
}; 

var runAsyncGenerator = function(generator) { 
    var asyncProcedureIterator = generator(); //create an iterator 
    asyncProcedureIterator.next(); //start the iterator 
    asyncProcedureIterator.next(asyncProcedureIterator); //pass a reference of the iterator to itself 
} 

var asyncPart1 = function(param1) { 
    var it = this; //the iterator will be equal to this. 
    console.log("Starting asyncPart1 with param1 = ", param1); 
    setTimeout(function() { 
     console.log("Done with asyncPart1"); 
     var returnValue = 42 + param1; 
     console.log("asyncPart1 returned ", returnValue); 
     it.next(returnValue); //when we are done, resume the iterator which has yielded to us. 
    },2000); 
}; 

var asyncPart2 = function(param1) { 
    var it = this; //the iterator will be equal to this. 
    console.log("Starting asyncPart2 with param1 = ", param1); 
    setTimeout(function() { 
     console.log("Done with asyncPart2"); 
     var returnValue = param1/2; 
     console.log("asyncPart2 returned ", returnValue); 
     //it.throw("Uh oh."); 

     it.next(returnValue); 

    },2000); 
}; 

var asyncPart3 = function(param1) { 
    var it = this; //the iterator will be equal to this. 
    console.log("Starting asyncPart3 with param1 = ", param1); 
    setTimeout(function() { 
     console.log("Done with asyncPart3"); 
     var returnValue = param1/3; 
     console.log("asyncPart3 returned ", returnValue); 
     it.next(returnValue); 
    },2000); 
}; 

runAsyncGenerator(asyncProcedureGenerator1); 

Chodzi o to, aby uruchomić generator, twórca iterator, a następnie przekazać odniesienie tego iteratora do siebie.

Następnie iterator może wywoływać funkcje asynchroniczne (z wydajnością) i przekazywać im odnośnik do samego siebie, który umożliwia powodzenie tych funkcji i wznowienie wykonywania przez wywołanie iterator.next (result) lub failure przez wywołanie iterator.throw (błąd).

Po prostu wymyśliłem ten wzorzec, więc mogą istnieć pewne błędy, których jeszcze nie znalazłem, ale wydaje się, że działają i pozwalają na bardzo płaski kod z minimalnymi dodatkami.

Powiązane problemy