ジェネレータ関数はJavaScriptの機能の中でもマイナーな機能で、function
の代わりにfunction*
で関数を作るのが特徴です(アロー関数版のジェネレータ関数式はありません)。また、その中ではyieid
式が使用可能になります。
ジェネレータ関数については、存在は知ってるけど実務で使ったことがないという方も多いのではないかと思います。
そこで、今回は筆者がこれまでの経験で唯一、ジェネレータ関数を実務で使ったケースを紹介します(具体的なユースケースについては実際の業務そのままではなくこの記事用に用意したシチュエーションとなるので、そこはご了承ください)。
配列の組み立てが複雑になりがち
例えば、「メインメニューの表示内容が、ユーザーが管理者ユーザーかどうかで異なる」という仕様を実装したい場合を考えましょう。普通に実装すると、こんな感じになるはずです。
function getMainMenu(user: User): Menu[] {
return [
{ name: 'Home', url: '/' },
{ name: 'Profile', url: '/profile' },
...(user.isAdmin ? [{ name: 'Admin', url: '/admin' }] : [])
];
}
このように、配列の中身が条件分岐によって決まるため、配列のスプレッド構文を使って条件分岐を実装したという方もいるでしょう。
問題は、さらに条件分岐が増えた場合です。
function getMainMenu(user: User): Menu[] {
return [
{ name: 'Home', url: '/' },
{ name: 'Profile', url: '/profile' },
...(user.isAdmin ? [{ name: 'Admin', url: '/admin' }] : []),
...(user.isSpecialUser ? [{ name: 'Special', url: '/special' }] : [])
];
}
さらに、条件分岐がネストする必要があったりすると、とんでもないことになります。
function getMainMenu(user: User): Menu[] {
return [
{ name: 'Home', url: '/' },
{ name: 'Profile', url: '/profile' },
...(user.isAdmin
? [
{ name: 'Admin', url: '/admin' },
...(user.isSpecialAdmin
? [{ name: 'Special Admin', url: '/special-admin' }]
: []),
]
: []),
...(user.isSpecialUser
? [{ name: 'Special', url: '/special' }]
: [])
];
}
🤮
ジェネレータ関数による改善
このように配列の組み立てが複雑になってしまう場合、Array.from
とジェネレータ関数を組み合わせると便利です。この例くらいだと配列のpushメソッドとかを使ってもいいのですが、配列を生成する部分を独立した関数に分けずにインラインで済ませたい場合とか、何が何でも配列を破壊的変更したくない場合はこの方法が役に立ちます。
function getMainMenu(user: User): Menu[] {
return Array.from(function*() {
yield { name: 'Home', url: '/' };
yield { name: 'Profile', url: '/profile' };
if (user.isAdmin) {
yield { name: 'Admin', url: '/admin' };
if (user.isSpecialAdmin) {
yield { name: 'Special Admin', url: '/special-admin' };
}
}
if (user.isSpecialUser) {
yield { name: 'Special', url: '/special' };
}
}());
}
🤩
ちなみに、yield*
を適宜活用するのも便利です。
function getMainMenu(user: User): Menu[] {
return Array.from(function*() {
yield* [
{ name: 'Home', url: '/' },
{ name: 'Profile', url: '/profile' },
];
if (user.isAdmin) {
yield { name: 'Admin', url: '/admin' };
if (user.isSpecialAdmin) {
yield { name: 'Special Admin', url: '/special-admin' };
}
}
if (user.isSpecialUser) {
yield { name: 'Special', url: '/special' };
}
}());
}
😍
以上です。