Skip to content

Instantly share code, notes, and snippets.

@gaogao-9
Last active March 21, 2016 02:28
Show Gist options
  • Save gaogao-9/a2a1f29d3c5ff201c668 to your computer and use it in GitHub Desktop.
Save gaogao-9/a2a1f29d3c5ff201c668 to your computer and use it in GitHub Desktop.
イベントを賢く捌くPromiseの話
const eventMap = new WeakMap();
const asyncOnSymbol = Symbol("asyncOn");
const asyncOffSymbol = Symbol("asyncOff");
function asyncOn(type, asyncFunc, useCapture){
const undefined = void 0;
if(eventMap.has(asyncFunc)) return;
return new Promise((resolve, reject)=>{
const _this = this;
eventMap.set(asyncFunc, onEventHandler);
this.addEventListener(type, onEventHandler, useCapture);
function onEventHandler(eve){
let promise;
try{
const promiseOrResult = asyncFunc.call(this, eve);
// 通常関数だった場合はそのまま処理を終える
if(!(promiseOrResult instanceof Promise)){
const result = promiseOrResult;
// 値が返って無ければ何もしない(=> イベントを継続する)
if(result === undefined) return result;
// 値が返っていたら、自分自身のイベント登録を解除する
_this[asyncOffSymbol](type, asyncFunc, useCapture);
// そして、Promiseを完了させる
resolve(result);
return;
}
// Promiseなら後続処理に回す
promise = promiseOrResult;
}
catch(err){
// 何らかの例外が発生した場合、そのイベントの解除を試みる
_this[asyncOffSymbol](type, asyncFunc, useCapture);
// そして、Promiseを完了させる
reject(err);
return;
}
promise
.then(result=>{
// 値が返って無ければ何もしない(=> イベントを継続する)
if(result === undefined) return;
// 値が返っていたら、自分自身のイベント登録を解除する
_this[asyncOffSymbol](type, asyncFunc, useCapture);
// そして、Promiseを完了させる
resolve(result);
})
.catch(err=>{
// 何らかの例外が発生した場合、そのイベントの解除を試みる
_this[asyncOffSymbol](type, asyncFunc, useCapture);
// そして、Promiseを完了させる
reject(err);
});
}
});
}
function asyncOff(type, asyncFunc, useCapture){
const onEventHandler = eventMap.get(asyncFunc);
if(!onEventHandler) return;
eventMap.delete(asyncFunc);
this.removeEventListener(type, onEventHandler, useCapture);
}
Object.defineProperties(EventTarget.prototype, {
[asyncOnSymbol]: {
value: asyncOn,
},
[asyncOffSymbol]: {
value: asyncOff,
},
});
module.exports = {on: asyncOnSymbol, off: asyncOffSymbol};
// 使用例
const {on, off} = require("asyncEvent.js");
// 同期処理の実行例
document[on]("click", function(eve){
console.log("同期処理の実行");
}, false).catch(err=>{
// エラーが発生したたらcatchでつなげて取得できる
console.error(err.stack);
});
// 同期処理の実行例(1回だけ実行されるイベント)
document[on]("click", function(eve){
console.log("1回だけ実行される同期処理の実行");
// undefined以外の値を返した段階で、このイベントが自動的に終了する
return "sync done";
}, false).then(data=>{
// thenでつなげたものは、イベントが終了した時に返した値になる
console.log(data); // "sync done"
}).catch(err=>{
// エラーが発生したたらcatchでつなげて取得できる
console.error(err.stack);
});
// 非同期処理の実行例
document[on]("click", async function(eve){
console.log("非同期処理の実行(1秒待ちます)");
await new Promise(resolve=>setTimeout(resolve, 1000));
console.log("非同期処理の実行(1秒経ちました)");
}, false).catch(err=>{
// エラーが発生したたらcatchでつなげて取得できる
console.error(err.stack);
});
// 非同期処理の実行例(1回だけ実行されるイベント)
document[on]("click", async function asyncOneCallback(eve){
// 非同期が絡む場合、2回発火しないように明示的にイベントを解除する必要がある
document[off]("click", asyncOneCallback, false);
console.log("1回だけ実行される非同期処理の実行(2秒待ちます)");
await new Promise(resolve=>setTimeout(resolve, 2000));
console.log("1回だけ実行される非同期処理の実行(2秒経ちました");
return "async done";
}, false).then(data=>{
// thenでつなげたものは、イベントが終了した時に返した値になる
console.log(data); // "async done"
}).catch(err=>{
// エラーが発生したたらcatchでつなげて取得できる
console.error(err.stack);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment