小ネタです。
古典的なMVC2の実装をNode(Express)でするとなった時、Viewでモデルの振る舞いを呼び出すときにその処理が非同期だった場合詰みがちである。
故に振る舞いをもったモデルの代わりに解決済みのプレーンなデータをViewに渡す実装や設計が多くみられるのだが、そうなるとやはりプレゼンター的な層ができあがってしまいView内とそこで似たようなループ処理などが発生しがちである。
こういった問題を避けるためにそもそもMVC2じゃないアーキテクチャを使ったりいろんな解決方法があるわけだが、そもそもViewが非同期に対応してればいいんじゃないという漠然とした思いがあった。
ってわけで色々調べてるとどうやら古き懐かしのejsには非同期を扱うためのオプションがあるらしい。
async: When true, EJS will use an async function for rendering. (Depends on async/await support in the JS runtime.
ただexpress-generatorで単純にejsを指定するだけで使えるってわけではなかったのでExpressのソースを読みながらasyncオプションを設定できるようちょっと調整した
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
/* ここのところ追加 */
app.engine('ejs', async function (path, option, callback) {
try {
let html = await ejs.renderFile(path, option, {async: true});
return callback(null, html);
} catch (err) {
return callback(err);
}
});
で、試しにとテンプレ描画するところで非同期関数を渡してみる
1秒後にomataseって文字列返すように実装
router.get('/', function(req, res, next) {
res.render('index', { asyncMethod: function () {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('omatase!'), 1000);
});
}});
});
で、テンプレ内でawaitしてみる。
うーん、viewでawait見るとキモくて草
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>テストアプリ</title>
<link rel='stylesheet' href='/stylesheets/style.css'>
</head>
<body>
<p><%= await asyncMethod(); %></p>
<%- await include('./_partial', {asyncMethod}) %>
</body>
</html>
パーシャルもpromise返すようになるのでawaitしてあげる
partialの中でも当然awaitつかえるんだよねって確認コード
<p>hello</p>
<p><%= await asyncMethod() %></p>
試してみたらちゃんと動いてて草
レスポンスもちょうどawait2回分で2秒になっていた。
ほーん、やるじゃん。 railsつかお。