UPDATE 「即時関数」って連呼してたけど、非同期処理に対する同期処理の意味だった。言葉間違えてました!
jQuery の Deferred オブジェクトで Promise の実装をしていて、Deferred オブジェクト生成部分を共通化したくなったので、高階関数化して同期処理を Promise に変換する関数をつくってみた。
// Underscore.js と jQuery が必要。
var promisify = function (func) {
var funcPartial = function () {
var funcArgs = _.toArray(arguments);
var dfr = new $.Deferred(), promiseArgs = [ dfr.resolve, dfr.reject ];
var timeoutId = setTimeout(function () {
clearTimeout(timeoutId);
func.apply(undefined, _.union(promiseArgs, funcArgs));
}, 1);
return dfr.promise();
};
return funcPartial;
};
これを使うと例えば;
// 画像プリロードをする処理を上記高階関数内で定義して;
var imageLoaded = promisify(function (resolve, reject, url) {
return $(document.createElement('img'))
.on('load', resolve)
.on('error', reject)
.attr('src', url);
});
// 画像プリロード結果を返す Promise を生成できる。
imageLoaded('http://lorempixel.com/400/200/');
.then(function (ev) {
var $el = $(ev.target), width = $el[0].naturalWidth, height = $el[0].naturalHeight;
console.log('完了!', width, height);
}, function (err) {
console.error('エラー!', err.message);
})
この高階関数 promisify
を使って関数を定義すれば、単純な値から同期処理、イベント等全ての処理を、非同期処理として Promise を返すようにできる。
また、Deferred オブジェクトの生成過程をカリー化して集約しているので、jQuery の Deferred オブジェクトから Q や ES6 の Promise に変更したい時も、関数一つ修正すれば、全ての Promise の内部処理を入れ替えられる。
かなり便利! だと思う。