async/await前提のライブラリ(例:Puppeteer)を使っていると、awaitをネストしたくなることがあります。例えば次のような状況です。
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
class Foo {
async getBar() {
console.log('getBar()');
return new Promise(resolve => resolve(new Bar()));
}
}
class Bar {
async baz() {
console.log('baz()');
await sleep(5000);
console.log('baz finished');
}
}
(async () => {
const foo = new Foo();
await (await foo.getBar()).baz();
})();
Barオブジェクトのメソッドbaz()をawaitしたいが、そのBarオブジェクトを得るのにfoo.getBar()をawaitで受け取る必要がある、という状況です。
個人的には、この例のようにawaitがネストしているとカッコ悪いなあ、と感じてしまいます。
await foo.getBar()
を一回変数に受け取ってもいいんですけど、他に書き方は無いのだろうか、ということで考えてみました。
thenでメソッドチェーンする
下記のようにすればawait1回でやりたいことが実現できます。
(async () => {
const foo = new Foo();
await foo.getBar()
.then(x => x.baz());
})();
このように書くとthen()の返すPromiseをawaitすることになるので、awaitでthen()の終了を待つことになります。
これで読みやすくなったかは疑問ですが、awaitが三重四重になるようであればこのような書き方もアリかなと思います。
Puppeteerでの実例
上のような状況の例としてPuppeteerのコードを示します。
Puppeteerで、ページ中の要素の出現を待ってクリックする、というような状況では次のようなコードを書くことがあります。
const button = await page.waitForSelector('button[type="submit"]', {visible: true});
await button.click();
これを1行で書きたいとき、次のように書くことができます。
await page.waitForSelector('button[type="submit"]', {visible: true})
.then(el => el.click());
Puppeteerでのコーディングではセレクタ1個にそれほど重要な意味はないので、いちいち変数に取りたくない気持ちになるんですよね。