2015-04-17 5 views
5

Я экспериментирую с генераторами ES6 с помощью babel, и мне трудно понять, как (или если!) Я могу эффективно использовать функцию async на основе обратного вызова для вывода итератора.Генераторы ES6: преобразование обратных вызовов к итераторам

Предположим, я хочу иметь возможность писать функцию, которая принимает несколько URL-адресов, асинхронно загружает их и возвращает их, как только они будут загружены. Я хотел бы быть в состоянии написать что-то вроде следующего:

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); 
} 

Как я могу реализовать downloadUrls? В идеале я хотел бы иметь возможность написать следующее:

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

Это, конечно, не работает, так как `` выход «» в настоящее время вызывается внутри обратного вызова, а не непосредственно внутри генератора. Я могу найти множество примеров онлайн-пользователей, которые пытаются использовать то же самое, они либо not much transparent), требуют enabling browser/node flags, либо используют специфичные для узла функции/библиотеки. Библиотека, ближайшая к тому, что мне нужно, это task.js, но я не могу иметь даже самый простой пример для текущего Chrome.

Есть ли способ получить предполагаемое поведение с использованием стандартных и текущих функций (с текущим я имею в виду использование транспилеров, таких как babel, но без необходимости включения дополнительных флагов в браузере), или мне нужно ждать async/await ?

+0

Как именно эти онлайн-объяснения не «прозрачны» для вас?Статья Дэвида Уолша является одной из лучших, которые я прочитал (но вам нужно будет прочитать всю серию курсов). – Bergi

+0

Похоже, что предложение генератора асинхронного вызова https://github.com/jhusain/asyncgenerator (еще нигде не поддерживается НАСКОЛЬКО МНЕ ИЗВЕСТНО). –

ответ

1

Есть ли способ, чтобы получить предполагаемое поведение, используя стандартные и текущие функции

Да, использовать обещание и генераторы. Многие библиотеки обещаний и некоторые автономные функции используют генератор «сопрограммы».

Но обратите внимание, что you cannot mix iteration with asynchrony, вы можете использовать генераторы только для. Ваш пример, похоже, немного смущает их - похоже, вы ожидаете, что цикл for ({url, data} of downloadUrls(urls)) { будет работать синхронно, что не может работать.

Должен ли я ждать async/await?

Нет, вам не обязательно ждать, Babel already supports them!

1

Вот чистый способ использовать генератор/итератор выравниваться асинхронный код, который работает для меня в 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); 

Идея заключается в том, чтобы запустить генератор, создатель итератора, а затем передать ссылку на этот итератор на себя.

Затем итератор может вызывать асинхронные функции (с выходом) и передавать им ссылку на себя, которая позволяет этим функциям либо возвращать успех, либо возобновлять выполнение, вызывая iterator.next (результат) или сбой, вызывая iterator.throw (ошибка).

Я только что придумал этот шаблон, поэтому могут быть некоторые ошибки, которые я еще не нашел, но он работает и позволяет очень плоский код с минимальными дополнениями.

Смежные вопросы