JavaScript ã® async generator functions ãæ·±ãæãäžããéåæã€ãã¬ãŒã·ã§ã³ãããã³ã«ããŠãŒã¹ã±ãŒã¹ãããã³ææ°ã® Web éçºã®å®è·µçãªäŸãæ¢æ±ããŸãã
Async Generator Functions: éåæã€ãã¬ãŒã·ã§ã³ãããã³ã«ã®ç¿åŸ
éåæããã°ã©ãã³ã°ã¯ãææ°ã® JavaScript éçºã®åºç€ã§ãããç¹ã« API ããã®ããŒã¿ã®ãã§ããããã¡ã€ã«ã®èªã¿åããããŒã¿ããŒã¹ãšã®å¯Ÿè©±ãªã©ã® I/O æäœãæ±ãå Žåã«éèŠã§ããåŸæ¥ããããã®éåæã¿ã¹ã¯ã管çããããã« Promises ãš async/await ã«äŸåããŠããŸãããããããasync generator functions ã¯ãéåæã€ãã¬ãŒã·ã§ã³ãåŠçããããã®åŒ·åãã€ãšã¬ã¬ã³ããªæ¹æ³ãæäŸããéåæãã€å¹ççã«ããŒã¿ã¹ããªãŒã ãåŠçã§ããããã«ããŸãã
éåæã€ãã¬ãŒã·ã§ã³ãããã³ã«ã®çè§£
async generator functions ã«é£ã³èŸŒãåã«ãããããæ§ç¯ãããŠããéåæã€ãã¬ãŒã·ã§ã³ãããã³ã«ãçè§£ããããšãäžå¯æ¬ ã§ãããããã®ãããã³ã«ã¯ãéåæããŒã¿ãœãŒã¹ãå¶åŸ¡ãããäºæž¬å¯èœãªæ¹æ³ã§å埩åŠçããæ¹æ³ãå®çŸ©ããŸãã
éåæã€ãã©ãã«ãããã³ã«
éåæã€ãã©ãã«ãããã³ã«ã¯ãéåæçã«å埩åŠçã§ãããªããžã§ã¯ããå®çŸ©ããŸãããªããžã§ã¯ãã¯ãSymbol.asyncIterator
ã§ããŒä»ããããã¡ãœãããæã¡ãéåæã€ãã¬ãŒã¿ãè¿ãå Žåã«ããã®ãããã³ã«ã«æºæ ããŸãã
ã€ãã©ãã«ãæ²ã®ãã¬ã€ãªã¹ãã®ããã«èããŠãã ãããéåæã€ãã©ãã«ã¯ãåçããåã«åæ²ãïŒéåæçã«ïŒããŒãããå¿ èŠããããã¬ã€ãªã¹ãã®ãããªãã®ã§ãã
äŸ:
const asyncIterable = {
[Symbol.asyncIterator]() {
return {
next() {
// Asynchronously fetch the next value
}
};
}
};
éåæã€ãã¬ãŒã¿ãããã³ã«
éåæã€ãã¬ãŒã¿ãããã³ã«ã¯ãéåæã€ãã¬ãŒã¿ãå®è£
ããå¿
èŠãããã¡ãœãããå®çŸ©ããŸãããã®ãããã³ã«ã«æºæ ãããªããžã§ã¯ãã¯ãnext()
ã¡ãœãããšããªãã·ã§ã³ã§ return()
ããã³ throw()
ã¡ãœãããæã€å¿
èŠããããŸãã
- next(): ãã®ã¡ãœããã¯ã
value
ãšdone
ã® 2 ã€ã®ããããã£ãæã€ãªããžã§ã¯ãã«è§£æ±ºããã Promise ãè¿ããŸããvalue
ã«ã¯ã·ãŒã±ã³ã¹å ã®æ¬¡ã®å€ãå«ãŸããdone
ã¯ã€ãã¬ãŒã·ã§ã³ãå®äºãããã©ããã瀺ãããŒã«å€ã§ãã - return(): (ãªãã·ã§ã³) ãã®ã¡ãœããã¯ã
value
ããã³done
ããããã£ãæã€ãªããžã§ã¯ãã«è§£æ±ºããã Promise ãè¿ããŸããããã¯ãã€ãã¬ãŒã¿ãéããããŠããããšã瀺ããŸããããã¯ããªãœãŒã¹ãè§£æŸããã®ã«åœ¹ç«ã¡ãŸãã - throw(): (ãªãã·ã§ã³) ãã®ã¡ãœããã¯ããšã©ãŒã§æåŠããã Promise ãè¿ããŸããããã¯ãã€ãã¬ãŒã·ã§ã³äžã«ãšã©ãŒãçºçããããšãéç¥ããããã«äœ¿çšãããŸãã
äŸ:
const asyncIterator = {
next() {
return new Promise((resolve) => {
// Asynchronously fetch the next value
setTimeout(() => {
resolve({ value: /* some value */, done: false });
}, 100);
});
},
return() {
return Promise.resolve({ value: undefined, done: true });
},
throw(error) {
return Promise.reject(error);
}
};
Async Generator Functions ã®ç޹ä»
Async generator functions ã¯ãéåæã€ãã¬ãŒã¿ãšã€ãã©ãã«ãäœæããããã®ãã䟿å©ã§èªã¿ãããæ¹æ³ãæäŸããŸãããããã¯ããžã§ãã¬ãŒã¿ã®åãš Promises ã®éåææ§ãçµã¿åããããã®ã§ãã
æ§æ
Async generator function ã¯ãasync function*
æ§æã䜿çšããŠå®£èšãããŸãã
async function* myAsyncGenerator() {
// Asynchronous operations and yield statements here
}
yield
ããŒã¯ãŒã
Async generator function å
ã§ã¯ãyield
ããŒã¯ãŒãã䜿çšããŠå€ãéåæçã«çæããŸããå yield
ã¹ããŒãã¡ã³ãã¯ãyield ããã Promise ã解決ããããŸã§ããžã§ãã¬ãŒã¿é¢æ°ã®å®è¡ã广çã«äžæåæ¢ããŸãã
äŸ:
async function* fetchUsers() {
const user1 = await fetch('https://example.com/api/users/1').then(res => res.json());
yield user1;
const user2 = await fetch('https://example.com/api/users/2').then(res => res.json());
yield user2;
const user3 = await fetch('https://example.com/api/users/3').then(res => res.json());
yield user3;
}
for await...of
ã䜿çšãã Async Generators ã®æ¶è²»
for await...of
ã«ãŒãã䜿çšããŠãasync generator function ã«ãã£ãŠçæãããå€ãå埩åŠçã§ããŸãããã®ã«ãŒãã¯ããžã§ãã¬ãŒã¿ã«ãã£ãŠ yield ããã Promises ã®éåæè§£æ±ºãèªåçã«åŠçããŸãã
äŸ:
async function main() {
for await (const user of fetchUsers()) {
console.log(user);
}
}
main();
Async Generator Functions ã®å®è·µçãªãŠãŒã¹ã±ãŒã¹
Async generator functions ã¯ã次ã®ãããªéåæããŒã¿ã¹ããªãŒã ãå«ãã·ããªãªã§åªããŠããŸãã
1. API ããã®ããŒã¿ã®ã¹ããªãŒãã³ã°
ããŒãžããŒã·ã§ã³ããµããŒããã API ãã倧ããªããŒã¿ã»ããããã§ããããããšãæ³åããŠã¿ãŠãã ãããããŒã¿ã»ããå šäœãäžåºŠã«ãã§ãããã代ããã«ãasync generator function ã䜿çšããŠãããŒã¿ã®ããŒãžã段éçã«ãã§ããããã³ yield ã§ããŸãã
äŸ (ããŒãžããŒã·ã§ã³ãããããŒã¿ã®ãã§ãã):
async function* fetchPaginatedData(url, pageSize = 10) {
let page = 1;
while (true) {
const response = await fetch(`${url}?page=${page}&pageSize=${pageSize}`);
const data = await response.json();
if (data.length === 0) {
return; // No more data
}
for (const item of data) {
yield item;
}
page++;
}
}
async function main() {
for await (const item of fetchPaginatedData('https://api.example.com/data')) {
console.log(item);
}
}
main();
åœéçãªäŸ (é貚æç®ã¬ãŒã API):
async function* fetchExchangeRates(currencyPair, startDate, endDate) {
let currentDate = new Date(startDate);
while (currentDate <= new Date(endDate)) {
const dateString = currentDate.toISOString().split('T')[0]; // YYYY-MM-DD
const url = `https://api.exchangerate.host/${dateString}?base=${currencyPair.substring(0,3)}&symbols=${currencyPair.substring(3,6)}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.success) {
yield {
date: dateString,
rate: data.rates[currencyPair.substring(3,6)],
};
}
} catch (error) {
console.error(`Error fetching data for ${dateString}:`, error);
// You might want to handle errors differently, e.g., retry or skip the date.
}
currentDate.setDate(currentDate.getDate() + 1);
}
}
async function main() {
const currencyPair = 'EURUSD';
const startDate = '2023-01-01';
const endDate = '2023-01-10';
for await (const rate of fetchExchangeRates(currencyPair, startDate, endDate)) {
console.log(rate);
}
}
main();
ãã®äŸã§ã¯ãæå®ãããæ¥ä»ç¯å²ã®æ¯æ¥ã® EUR ãã USD ãžã®çºæ¿ã¬ãŒãããã§ããããŸããAPI åŒã³åºãäžã®æœåšçãªãšã©ãŒãåŠçããŸããä¿¡é Œã§ããé©å㪠API ãšã³ããã€ã³ãã§ `https://api.exchangerate.host` ã眮ãæããããšãå¿ããªãã§ãã ããã
2. å€§èŠæš¡ãªãã¡ã€ã«ã®åŠç
å€§èŠæš¡ãªãã¡ã€ã«ãæ±ãå Žåããã¡ã€ã«å šäœãã¡ã¢ãªã«èªã¿èŸŒããšéå¹çã«ãªãå¯èœæ§ããããŸããAsync generator functions ã䜿çšãããšããã¡ã€ã«ã 1 è¡ãã€ãŸãã¯ãã£ã³ã¯åäœã§èªã¿åããåãã£ã³ã¯ãéåæçã«åŠçã§ããŸãã
äŸ (å€§èŠæš¡ãªãã¡ã€ã«ã 1 è¡ãã€èªã¿åã - Node.js):
const fs = require('fs');
const readline = require('readline');
async function* readLines(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
yield line;
}
}
async function main() {
for await (const line of readLines('large_file.txt')) {
// Process each line asynchronously
console.log(line);
}
}
main();
ãã® Node.js ã®äŸã§ã¯ãfs.createReadStream
ãš readline.createInterface
ã䜿çšããŠãã¡ã€ã«ã 1 è¡ãã€èªã¿åãæ¹æ³ã瀺ããŸããreadLines
async generator function ã¯ãåè¡ãéåæçã« yield ããŸãã
3. ãªã¢ã«ã¿ã€ã ããŒã¿ã¹ããªãŒã ã®åŠç (WebSocketsãServer-Sent Events)
Async generator functions ã¯ãWebSockets ã Server-Sent Events (SSE) ãªã©ã®ãœãŒã¹ããã®ãªã¢ã«ã¿ã€ã ããŒã¿ã¹ããªãŒã ã®åŠçã«é©ããŠããŸããã¹ããªãŒã ããããŒã¿ãå°çãããšãç¶ç¶çã«ããŒã¿ã yield ã§ããŸãã
äŸ (WebSocket ããã®ããŒã¿ã®åŠç - æŠå¿µ):
// This is a conceptual example and requires a WebSocket library like 'ws' (Node.js) or the browser's built-in WebSocket API.
async function* processWebSocketStream(url) {
const websocket = new WebSocket(url);
websocket.onmessage = (event) => {
//This needs to be handled outside the generator.
//Typically, you'd push the event.data into a queue
//and the generator would asynchronously pull from the queue
//via a Promise that resolves when data is available.
};
websocket.onerror = (error) => {
//Handle errors.
};
websocket.onclose = () => {
//Handle close.
}
//The actual yielding and queue management would happen here,
//making use of Promises to synchronize between the websocket.onmessage
//event and the async generator function.
//This is a simplified illustration.
//while(true){ //Use this if properly queuing events.
// const data = await new Promise((resolve) => {
// // Resolve the promise when data is available in the queue.
// })
// yield data
//}
}
async function main() {
// for await (const message of processWebSocketStream('wss://example.com/ws')) {
// console.log(message);
// }
console.log("WebSocket example - conceptual only. See comments in code for details.");
}
main();
WebSocket ã®äŸã«é¢ããéèŠãªæ³šæ:
- æäŸãããŠãã WebSocket ã®äŸã¯ãWebSocket ã®ã€ãã³ãé§ååæ§è³ªã async generator ãšçŽæ¥çµ±åããã«ã¯ãPromise ãšãã¥ãŒã䜿çšããæ éãªåæãå¿ èŠã«ãªããããäž»ã«æŠå¿µçãªãã®ã§ãã
- å®éã®å®è£ ã§ã¯éåžžãåä¿¡ WebSocket ã¡ãã»ãŒãžããã¥ãŒã«ãããã¡ãªã³ã°ããæ°ããããŒã¿ãå©çšå¯èœã«ãªã£ããšãã« async generator ã«éç¥ããããã« Promise ã䜿çšããŸããããã«ããããžã§ãã¬ãŒã¿ãŒã¯ããŒã¿ã®åŸ æ©äžã«ãããã¯ãããŸããã
4. ã«ã¹ã¿ã éåæã€ãã¬ãŒã¿ã®å®è£
Async generator functions ã䜿çšãããšãä»»æã®éåæããŒã¿ãœãŒã¹çšã®ã«ã¹ã¿ã éåæã€ãã¬ãŒã¿ãç°¡åã«äœæã§ããŸããå€ããã§ãããåŠçãããã³ yield ããããã®ç¬èªã®ããžãã¯ãå®çŸ©ã§ããŸãã
äŸ (æ°å€ã®ã·ãŒã±ã³ã¹ãéåæçã«çæ):
async function* generateNumbers(start, end, delay) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, delay));
yield i;
}
}
async function main() {
for await (const number of generateNumbers(1, 5, 500)) {
console.log(number);
}
}
main();
ãã®äŸã§ã¯ãstart
ãã end
ãŸã§ã®æ°å€ã®ã·ãŒã±ã³ã¹ãçæããåæ°å€éã«æå®ããã delay
ãèšå®ããŸããawait new Promise(resolve => setTimeout(resolve, delay))
è¡ã¯ãéåæé
å»¶ãå°å
¥ããŸãã
ãšã©ãŒåŠç
Async generator functions ã䜿çšããå Žåã¯ããšã©ãŒåŠçãéèŠã§ãããžã§ãã¬ãŒã¿é¢æ°å
ã§ try...catch
ãããã¯ã䜿çšããŠãéåææäœäžã«çºçãããšã©ãŒãåŠçã§ããŸãã
äŸ (Async Generator ã§ã®ãšã©ãŒåŠç):
async function* fetchData(url) {
try {
const response = await fetch(url);
const data = await response.json();
yield data;
} catch (error) {
console.error('Error fetching data:', error);
// You can choose to re-throw the error, yield a default value, or stop the iteration.
// For example, yield { error: error.message };
throw error;
}
}
async function main() {
try {
for await (const data of fetchData('https://example.com/api/invalid')) {
console.log(data);
}
} catch (error) {
console.error('Error during iteration:', error);
}
}
main();
ãã®äŸã§ã¯ãfetch
æäœäžã«çºçããå¯èœæ§ã®ãããšã©ãŒãåŠçããæ¹æ³ã瀺ããŸããtry...catch
ãããã¯ã¯ããšã©ãŒããã£ããããŠã³ã³ãœãŒã«ã«èšé²ããŸãããžã§ãã¬ãŒã¿ã®ã³ã³ã·ã¥ãŒããŒã«ãã£ãŠãã£ãããããããã«ãšã©ãŒãåã¹ããŒãããããšã©ãŒãªããžã§ã¯ãã yield ãããããããšãã§ããŸãã
Async Generator Functions ã䜿çšããå©ç¹
- ã³ãŒãã®å¯èªæ§ã®åäž: Async generator functions ã¯ãåŸæ¥ã® Promise ããŒã¹ã®ã¢ãããŒããšæ¯èŒããŠãéåæã€ãã¬ãŒã·ã§ã³ã³ãŒããããèªã¿ããããä¿å®ããããããŸãã
- éåæå¶åŸ¡ãããŒã®ç°¡çŽ å: éåæããžãã¯ãããèªç¶ã§ã·ãŒã±ã³ã·ã£ã«ãªæ¹æ³ã§è¡šçŸã§ãããããæšè«ã容æã«ãªããŸãã
- å¹ççãªãªãœãŒã¹ç®¡ç: ãã£ã³ã¯ãŸãã¯ã¹ããªãŒã ã§ããŒã¿ãåŠçã§ãããããç¹ã«å€§èŠæš¡ãªããŒã¿ã»ãããŸãã¯ãªã¢ã«ã¿ã€ã ããŒã¿ã¹ããªãŒã ãæ±ãå Žåã«ãã¡ã¢ãªæ¶è²»ãåæžããããã©ãŒãã³ã¹ãåäžãããŸãã
- é¢å¿ã®æç¢ºãªåé¢: ããŒã¿ã®çæããžãã¯ãããŒã¿ã®æ¶è²»ããžãã¯ããåé¢ããã¢ãžã¥ãŒã«æ§ãšåå©çšæ§ãä¿é²ããŸãã
ä»ã®éåæã¢ãããŒããšã®æ¯èŒ
Async Generators vs. Promises
Promises ã¯éåææäœã®åºæ¬ã§ãããéåæå€ã®ã·ãŒã±ã³ã¹ã®åŠçã«ã¯ããŸãé©ããŠããŸãããAsync generators ã¯ãéåæããŒã¿ã¹ããªãŒã ãå埩åŠçããããã®ãããæ§é åãããå¹ççãªæ¹æ³ãæäŸããŸãã
Async Generators vs. RxJS Observables
RxJS Observables ã¯ãéåæããŒã¿ã¹ããªãŒã ãåŠçããããã®ãã 1 ã€ã®åŒ·åãªããŒã«ã§ããObservables ã¯ãããŒã¿ã¹ããªãŒã ã®å€æããã£ã«ã¿ãªã³ã°ãããã³çµåãè¡ãããã®æŒç®åãªã©ã®ããé«åºŠãªæ©èœãæäŸããŸãããã ããasync generator ã¯ãåºæ¬çãªéåæã€ãã¬ãŒã·ã§ã³ã·ããªãªã§ã¯éåžžãäœ¿ãæ¹ãç°¡åã§ãã
ãã©ãŠã¶ãš Node.js ã®äºææ§
Async generator functions ã¯ãææ°ã®ãã©ãŠã¶ãš Node.js ã§åºããµããŒããããŠããŸãããããã¯ãES2018 (ECMAScript 2018) ããµããŒããããã¹ãŠã®äž»èŠãªãã©ãŠã¶ãšãNode.js ããŒãžã§ã³ 10 以éã§äœ¿çšã§ããŸãã
Babel ãªã©ã®ããŒã«ã䜿çšããŠãå€ãç°å¢ããµããŒãããå¿ èŠãããå Žåã¯ãã³ãŒããå€ãããŒãžã§ã³ã® JavaScript ã«ãã©ã³ã¹ãã€ã«ã§ããŸãã
çµè«
Async generator functions ã¯ãJavaScript ã®éåæããã°ã©ãã³ã°ããŒã«ããããžã®è²Žéãªè¿œå ã§ãããããã¯ãéåæã€ãã¬ãŒã·ã§ã³ãåŠçããããã®åŒ·åãã€ãšã¬ã¬ã³ããªæ¹æ³ãæäŸããããŒã¿ã¹ããªãŒã ãå¹ççãã€ä¿å®å¯èœãªæ¹æ³ã§åŠçããããšã容æã«ããŸããéåæã€ãã¬ãŒã·ã§ã³ãããã³ã«ãš async generator functions ã®æ§æãçè§£ããããšã§ãAPI ããã®ããŒã¿ã®ã¹ããªãŒãã³ã°ãããå€§èŠæš¡ãªãã¡ã€ã«ã®åŠçããªã¢ã«ã¿ã€ã ããŒã¿ã¹ããªãŒã ã®åŠçãŸã§ãå¹ åºãã¢ããªã±ãŒã·ã§ã³ã§ãã®å©ç¹ã掻çšã§ããŸãã
ãããªãåŠç¿
- MDN Web Docs: AsyncGeneratorFunction
- Exploring ES2018: éåæã€ãã¬ãŒã·ã§ã³
- Node.js ããã¥ã¡ã³ã: ã¹ããªãŒã ããã³ãã¡ã€ã«ã·ã¹ãã æäœã«ã€ããŠã¯ãå ¬åŒã® Node.js ããã¥ã¡ã³ããåç §ããŠãã ããã