Как мы будем их использовать?
Первое, что нужно сделать — добавить ключевое слово async перед определением функции. Ключевое слово async не просто позволяет использовать await, но также говорит, что данная функция будет возвращать объект Promise. Какое бы значение вы не вернули из асинхронной функции, она всегда фактически вернет объект Promise с этим значением. Чтобы обещание выполнилось неуспешно, нужно бросить исключение. Например:
async function foo() { if( Math.round(Math.random()) ) return 'Success!'; else throw 'Failure!';}В любой момент внутри асинхронной функции вы можете добавить ключевое слово await, и выполнение функции остановится, пока обещание не будет успешно или неуспешно выполнено. В примере ниже await promisingOperation() вернет результат обещания:
function promisingOperation() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
if( Math.round(Math.random()) )
resolve('Success!');
else
reject('Failure!');
}, 1000);
}
}
async function foo() {
var message = await promisingOperation();
console.log(message);
}
Когда вы вызываете foo, она ждет выполнения функции promisingOperation и выводит либо «Success!», если обещание успешно, либо «Failure!», если неуспешно.
Остается один вопрос: как обработать неуспешное обещание? Нам нужно обернуть вызов в блок try... catch. Если одна из асинхронных операций завершится неуспешно, сработает блок catch.
async function foo() {
try {
var message = await promisingOperation();
console.log(message);
}
catch (e) {
console.log('We failed:', e);
}
}
Генераторы (generators). В чём заключаются, как работают, когда могут применяться?
Генераторы – новый вид функций в современном JavaScript. Они отличаются от обычных тем, что могут приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять его позже, в произвольный момент времени.
Создание генератора
Для объявления генератора используется новая синтаксическая конструкция: function* (функция со звёздочкой). Её называют «функция-генератор» (generator function). Выглядит это так:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
При запуске generateSequence() код такой функции не выполняется. Вместо этого она возвращает специальный объект, который как раз и называют «генератором».
let generator = generateSequence();
Правильнее всего будет воспринимать генератор как «замороженный вызов функции»:
При создании генератора код находится в начале своего выполнения.
Основным методом генератора является next(). При вызове он возобновляет выполнение кода до ближайшего ключевого слова yield. По достижении yield выполнение приостанавливается, а значение – возвращается во внешний код:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
let one = generator.next();
Повторный вызов generator.next() возобновит выполнение и вернёт результат следующего yield:
let two = generator.next();
И, наконец, последний вызов завершит выполнение функции и вернёт результат return:
let three = generator.next();
Функция завершена. Внешний код должен увидеть это из свойства done:true и обработать value:3, как окончательный результат.
Генератор – итератор
Как вы, наверно, уже догадались по наличию метода next(), генератор связан с итераторами. В частности, он является итерируемым объектом.
Его можно перебирать и через for..of:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1, затем 2
}
Заметим, однако, существенную особенность такого перебора! При запуске примера выше будет выведено значение 1, затем 2. Значение 3 выведено не будет. Это потому что стандартный перебор итератора игнорирует value на последнем значении, при done: true. Так что результат return в цикле for..of не выводится.
Соответственно, если мы хотим, чтобы все значения возвращались при переборе через for..of, то надо возвращать их через yield:
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1, затем 2, затем 3
}
Композиция генераторов
function* gen1() {
yield 1;
yield 2;
return 3;
}
// 4, 1, 2, 5
function* gen2() {
yield 4;
yield* gen1();
yield 5;
}
//4, 1, 2, 3, 5
function* gen3() {
yield 4;
yield yield* gen1();
yield 5;
}