Библиотека co , как и execute в примере выше, получает генератор и выполняет его. Начнём сразу с примера, а потом – детали и полезные возможности:
co(function*() {
let result = yield new Promise(
resolve => setTimeout(resolve, 1000, 1)
);
alert(result); // 1
})
Предполагается, что библиотека co подключена к странице , например, отсюда: http://cdnjs.com/libraries/co/ . В примере выше function*() делает
yield промиса с setTimeout , который через секунду возвращает 1 .
Вызов co(…) возвращает промис с результатом генератора. Если в примере выше function*() что‑то возвратит, то это можно будет получить через
.then в результате co :
co(function*() {
let result = yield new Promise(
resolve => setTimeout(resolve, 1000, 1)
);
return result; // return 1
}).then(alert); // 1
Библиотека co умеет выполнять не только промисы. Есть несколько видов значений, которые можно yield , и их обработает co :
Промис.
Объект‑генератор.
Функция‑генератор function*() – co её выполнит, затем выполнит полученный генератор.
Функция с единственным аргументом вида function(callback) – библиотека co её запустит со своей функцией‑ callback и будет ожидать, что при ошибке она вызовет callback(err) , а при успешном выполнении – callback(null, result) . То есть, в первом аргументе – будет ошибка (если есть), а втором – результат (если нет ошибки). После чего результат будет передан в генератор.
Массив или объект из вышеперечисленного. При этом все задачи будут выполнены параллельно, и результат, в той же структуре, будет выдан наружу.
В примере ниже происходит yield всех этих видов значений. Библиотека co обеспечивает их выполнение и возврат результата в генератор:
Object.defineProperty(window, 'result', {
// присвоение result=… будет выводить значение set: value => alert(JSON.stringify(value))
});
co(function*() {
result = yield function*() { // генератор return 1;
}();
result = yield function*() { // функция‐генератор return 2;
};
result = yield Promise.resolve(3); // промис
result = yield function(callback) { // function(callback) setTimeout(() => callback(null, 4), 1000);
};
result = yield { // две задачи выполнит параллельно, как Promise.all one: Promise.resolve(1),
two: function*() { return 2; }
};
result = yield [ // две задачи выполнит параллельно, как Promise.all Promise.resolve(1),
function*() { return 2 }
];
});
Посмотрим пример посложнее, с композицией генераторов:
co(function*() {
let result = yield* gen(); alert(result); // hello
});
function* gen() { return yield* gen2();
}
function* gen2() {
let result = yield new Promise( // (1)
resolve => setTimeout(resolve, 1000, 'hello')
);
return result;
}
Это – отличный вариант для библиотеки co . Композиция yield* gen() вызывает gen() в потоке внешнего генератора. Аналогично делает и yield* gen() .
Поэтому yield new Promise из строки (1) в gen2() попадает напрямую в библиотеку co , как если бы он был сделан так:
co(function*() {
// gen() и затем gen2 встраиваются во внешний генератор let result = yield new Promise(
resolve => setTimeout(resolve, 1000, 'hello')
);
alert(result); // hello
});
Пример showUserAvatar() можно переписать с использованием co вот так:
// Загрузить данные пользователя с нашего сервера function* fetchUser(url) {
let userFetch = yield fetch(url); let user = yield userFetch.json();
return user;
}
// Загрузить профиль пользователя с github function* fetchGithubUser(user) {
let githubFetch = yield fetch(`https://api.github.com/users/${user.name}`); let githubUser = yield githubFetch.json();
return githubUser;
}
// Подождать ms миллисекунд function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Использовать функции выше для получения аватара пользователя function* fetchAvatar(url) {
let user = yield* fetchUser(url);
let githubUser = yield* fetchGithubUser(user);
return githubUser.avatar_url;
}
// Использовать функции выше для получения и показа аватара function* showUserAvatar() {
let avatarUrl; try {
avatarUrl = yield* fetchAvatar('/article/generator/user.json');
} catch(e) {
avatarUrl = '/article/generator/anon.png';
}
let img = new Image(); img.src = avatarUrl;
img.className = "promise‐avatar‐example"; document.body.appendChild(img);
yield sleep(2000); img.remove();
return img.src;
}
co(showUserAvatar);
Заметим, что для перехвата ошибок при получении аватара используется try..catch вокруг yield* fetchAvatar :
try {
avatarUrl = yield* fetchAvatar('/article/generator/user.json');
} catch(e) {
avatarUrl = '/article/generator/anon.png';
}
Это – одно из главных удобств использования генераторов. Несмотря на то, что операции fetch – асинхронные, мы можем использовать обычный
try..catch для обработки ошибок в них.
Do'stlaringiz bilan baham: |