非同期処理を同期処理のように書きたい
というニーズがあるらしい。確かに、promise.then()
やawait
を何回も書くのが煩わしいという気持ちは分かる。
そこで、promise.then()
やawait
を使わずに、普通の代入文のみで非同期処理を書いてみようというのがこの記事の趣旨。
方法1:代入式の右辺が必ず値を返すようにする
例えば「promiseが解決されていれば値を返し、解決されていなければpromiseをthrowする」という方法がある。Reactで使われているらしい。よく知らない。
方法2:代入式の左辺がPromiseを受け取れるようにする
こっちが本題。
fetchやら何やらで値を取ってきて処理するとして、その値は最終的にHTMLの書き換えに使われる(ことが多い)はず。
というわけで、Element.prototype.innerHTMLを書き換えてpromiseを受け取れるようにする。
実装
Element.prototype.innerHTMLの書き換えについてはこちらのサイトを参考にさせて頂いた。
Promiseが解決されるまでは「loading...」と表示する。
{
const desc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML')
Object.defineProperty(Element.prototype, "innerHTML", {
get: function(){
return desc.get.call(this)
},
set: function(x){
if (x instanceof Promise || (x && typeof x.then === 'function')) {
//Promiseだった場合、解決されるまで'loading...'を表示する
desc.set.call(this, 'loading...')
x.then(val=>desc.set.call(this, val))
} else {
//Promise以外の場合、そのまま表示する
desc.set.call(this, x)
}
}
})
}
使用例
//1秒後に解決されるPromiseをinnerHTMLに代入
document.body.innerHTML = new Promise(resolve=>{
setTimeout(_=>resolve('resolveされました'), 1000)
})
これを実行すると、まず「loading...」が表示され、1秒後に「resolveされました」が表示される。
なお、実際に使うときは、
- promise内でエラーが出た場合(promise.catch()
)
- element.innerTextやelement.insertAdjacentHTML
への対応が必要だと思われる。