-
-
Save gaogao-9/f870dee8edc8bc03d687 to your computer and use it in GitHub Desktop.
async/awaitでDOMEventやMutationObserverのような再利用可能なイベントに対して綺麗に処理できるようなユーティリティメソッド考えた話( https://babeljs.io/repl/#?experimental=true&evaluate=true&loose=false&spec=true )で実行可能です
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Util{ | |
static createPromiseContainer(){ | |
return {resolve:null,reject:null,_resolved:[]}; | |
} | |
static createPromiseCallback(callback,promiseContainer){ | |
return (...args)=>{ | |
var isFirst = true; | |
for(var res of callback(...args)){ | |
if(typeof(res)==="undefined"){ | |
continue; | |
} | |
if(res instanceof Error){ | |
promiseContainer.reject(res); | |
break; | |
} | |
if(isFirst){ | |
promiseContainer.resolve(res); | |
isFirst = false; | |
} | |
else{ | |
promiseContainer._resolved.push(res); | |
} | |
} | |
}; | |
} | |
static *promiseGenerator(promiseContainer){ | |
var isDone = false; | |
while(!isDone){ | |
yield new Promise((resolve,reject)=>{ | |
promiseContainer.resolve = resolve; | |
promiseContainer.reject = reject; | |
if(promiseContainer._resolved.length) resolve(promiseContainer._resolved.shift()); | |
}).catch(err=>{ | |
isDone = true; | |
throw err; | |
}); | |
} | |
} | |
static *eventPromiseGenerator(target,type,generator){ | |
const promiseContainer = Util.createPromiseContainer(); | |
const callback = Util.createPromiseCallback(generator,promiseContainer); | |
target.addEventListener(type,callback,false); | |
yield* Util.promiseGenerator(promiseContainer); | |
target.removeEventListener(type,callback); | |
} | |
static *mutationPromiseGenerator(target,options,generator){ | |
const promiseContainer = Util.createPromiseContainer(); | |
const callback = Util.createPromiseCallback(generator,promiseContainer); | |
const mo = new MutationObserver(callback); | |
mo.observe(target,options); | |
yield* Util.promiseGenerator(promiseContainer); | |
mo.disconnect(); | |
} | |
} | |
// 糖衣構文としてEventTargetに対してprototype汚染する | |
const _ = Symbol(); | |
Object.defineProperties(EventTarget.prototype,{ | |
[_]:{ | |
get(){ | |
return this; | |
} | |
} | |
}); | |
Object.defineProperties(EventTarget.prototype[_],{ | |
promiseEvent:{ | |
value: function(type,generator){ | |
return Util.eventPromiseGenerator(this,type,generator); | |
} | |
}, | |
promiseObserver:{ | |
value: function(options,generator){ | |
return Util.mutationPromiseGenerator(this,options,generator); | |
} | |
} | |
}); | |
// ウインドウをclickした時に3回だけログを残す例 | |
(async function(){ | |
var cnt = 3; | |
const promiseIterator = window[_].promiseEvent("click",function*(eve){ | |
yield (cnt--) ? eve : new Error("老衰"); | |
}); | |
for(var promise of promiseIterator){ | |
try{ | |
var eve = await promise; | |
console.log(`クリックしたでw(時間:${eve.timeStamp})`); | |
} | |
catch(err){ | |
console.log(`しんだでw(死因:${err.message})`); | |
} | |
} | |
})(); | |
// こっち(左側)のtextareaのカーソル位置を動かすと(スタイルが変わるので) | |
// それを監視するMutationObserverの例 | |
(async function(){ | |
const $textarea = document.querySelector("textarea.ace_text-input"); | |
const promiseIterator = $textarea[_].promiseObserver({attributes:true,attributeFilter:["style"]},function*(data){ | |
for(var i=0;i<data.length;++i){ | |
// こんな風にイベントで得られるデータ配列をさらにyield可能…! | |
yield data[i]; | |
} | |
}); | |
for(var promise of promiseIterator){ | |
try{ | |
var data = await promise; | |
var style = Array.prototype.slice.call($textarea.style).map(x=>`${x}:${$textarea.style[x]}`); | |
console.log("属性変わったでw"); | |
} | |
catch(err){ | |
console.log(err.message); | |
} | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var _temporalUndefined = {}; | |
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | |
var Util = _temporalUndefined; | |
// 糖衣構文としてEventTargetに対してprototype汚染する | |
var _ = _temporalUndefined; | |
function _temporalAssertDefined(val, name, undef) { if (val === undef) { throw new ReferenceError(name + " is not defined - temporal dead zone"); } return true; } | |
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | |
function _instanceof(left, right) { if (right != null && right[Symbol.hasInstance]) { return right[Symbol.hasInstance](left); } else { return left instanceof right; } } | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
Util = (function () { | |
function Util() { | |
_classCallCheck(this, _temporalAssertDefined(Util, "Util", _temporalUndefined) && Util); | |
} | |
_createClass(_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util, null, [{ | |
key: "createPromiseContainer", | |
value: function createPromiseContainer() { | |
return { resolve: null, reject: null, _resolved: [] }; | |
} | |
}, { | |
key: "createPromiseCallback", | |
value: function createPromiseCallback(callback, promiseContainer) { | |
return function () { | |
var isFirst = true; | |
var _iteratorNormalCompletion = true; | |
var _didIteratorError = false; | |
var _iteratorError = undefined; | |
try { | |
for (var _iterator = callback.apply(undefined, arguments)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | |
var res = _step.value; | |
if (typeof res === "undefined") { | |
continue; | |
} | |
if (_instanceof(res, Error)) { | |
promiseContainer.reject(res); | |
break; | |
} | |
if (isFirst) { | |
promiseContainer.resolve(res); | |
isFirst = false; | |
} else { | |
promiseContainer._resolved.push(res); | |
} | |
} | |
} catch (err) { | |
_didIteratorError = true; | |
_iteratorError = err; | |
} finally { | |
try { | |
if (!_iteratorNormalCompletion && _iterator["return"]) { | |
_iterator["return"](); | |
} | |
} finally { | |
if (_didIteratorError) { | |
throw _iteratorError; | |
} | |
} | |
} | |
}; | |
} | |
}, { | |
key: "promiseGenerator", | |
value: regeneratorRuntime.mark(function promiseGenerator(promiseContainer) { | |
var isDone; | |
return regeneratorRuntime.wrap(function promiseGenerator$(context$2$0) { | |
while (1) switch (context$2$0.prev = context$2$0.next) { | |
case 0: | |
isDone = false; | |
case 1: | |
if (isDone) { | |
context$2$0.next = 6; | |
break; | |
} | |
context$2$0.next = 4; | |
return new Promise(function (resolve, reject) { | |
promiseContainer.resolve = resolve; | |
promiseContainer.reject = reject; | |
if (promiseContainer._resolved.length) resolve(promiseContainer._resolved.shift()); | |
})["catch"](function (err) { | |
isDone = true; | |
throw err; | |
}); | |
case 4: | |
context$2$0.next = 1; | |
break; | |
case 6: | |
case "end": | |
return context$2$0.stop(); | |
} | |
}, promiseGenerator, this); | |
}) | |
}, { | |
key: "eventPromiseGenerator", | |
value: regeneratorRuntime.mark(function eventPromiseGenerator(target, type, generator) { | |
var promiseContainer = _temporalUndefined; | |
var callback = _temporalUndefined; | |
return regeneratorRuntime.wrap(function eventPromiseGenerator$(context$2$0) { | |
while (1) switch (context$2$0.prev = context$2$0.next) { | |
case 0: | |
promiseContainer = (_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).createPromiseContainer(); | |
callback = (_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).createPromiseCallback(generator, _temporalAssertDefined(promiseContainer, "promiseContainer", _temporalUndefined) && promiseContainer); | |
target.addEventListener(type, _temporalAssertDefined(callback, "callback", _temporalUndefined) && callback, false); | |
return context$2$0.delegateYield((_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).promiseGenerator(_temporalAssertDefined(promiseContainer, "promiseContainer", _temporalUndefined) && promiseContainer), "t0", 4); | |
case 4: | |
target.removeEventListener(type, _temporalAssertDefined(callback, "callback", _temporalUndefined) && callback); | |
case 5: | |
case "end": | |
return context$2$0.stop(); | |
} | |
}, eventPromiseGenerator, this); | |
}) | |
}, { | |
key: "mutationPromiseGenerator", | |
value: regeneratorRuntime.mark(function mutationPromiseGenerator(target, options, generator) { | |
var promiseContainer = _temporalUndefined; | |
var callback = _temporalUndefined; | |
var mo = _temporalUndefined; | |
return regeneratorRuntime.wrap(function mutationPromiseGenerator$(context$2$0) { | |
while (1) switch (context$2$0.prev = context$2$0.next) { | |
case 0: | |
promiseContainer = (_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).createPromiseContainer(); | |
callback = (_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).createPromiseCallback(generator, _temporalAssertDefined(promiseContainer, "promiseContainer", _temporalUndefined) && promiseContainer); | |
mo = new MutationObserver(_temporalAssertDefined(callback, "callback", _temporalUndefined) && callback); | |
(_temporalAssertDefined(mo, "mo", _temporalUndefined) && mo).observe(target, options); | |
return context$2$0.delegateYield((_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).promiseGenerator(_temporalAssertDefined(promiseContainer, "promiseContainer", _temporalUndefined) && promiseContainer), "t0", 5); | |
case 5: | |
(_temporalAssertDefined(mo, "mo", _temporalUndefined) && mo).disconnect(); | |
case 6: | |
case "end": | |
return context$2$0.stop(); | |
} | |
}, mutationPromiseGenerator, this); | |
}) | |
}]); | |
return _temporalAssertDefined(Util, "Util", _temporalUndefined) && Util; | |
})(); | |
_ = Symbol(); | |
Object.defineProperties(EventTarget.prototype, _defineProperty({}, _temporalAssertDefined(_, "_", _temporalUndefined) && _, { | |
get: function get() { | |
return this; | |
} | |
})); | |
Object.defineProperties(EventTarget.prototype[_temporalAssertDefined(_, "_", _temporalUndefined) && _], { | |
promiseEvent: { | |
value: function value(type, generator) { | |
return (_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).eventPromiseGenerator(this, type, generator); | |
} | |
}, | |
promiseObserver: { | |
value: function value(options, generator) { | |
return (_temporalAssertDefined(Util, "Util", _temporalUndefined) && Util).mutationPromiseGenerator(this, options, generator); | |
} | |
} | |
}); | |
// ウインドウをclickした時に3回だけログを残す例 | |
(function callee$0$0() { | |
var promiseIterator = _temporalUndefined; | |
var cnt, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, promise, eve; | |
return regeneratorRuntime.async(function callee$0$0$(context$1$0) { | |
while (1) switch (context$1$0.prev = context$1$0.next) { | |
case 0: | |
cnt = 3; | |
promiseIterator = window[_temporalAssertDefined(_, "_", _temporalUndefined) && _].promiseEvent("click", regeneratorRuntime.mark(function callee$1$0(eve) { | |
return regeneratorRuntime.wrap(function callee$1$0$(context$2$0) { | |
while (1) switch (context$2$0.prev = context$2$0.next) { | |
case 0: | |
context$2$0.next = 2; | |
return cnt-- ? eve : new Error("老衰"); | |
case 2: | |
case "end": | |
return context$2$0.stop(); | |
} | |
}, callee$1$0, this); | |
})); | |
_iteratorNormalCompletion2 = true; | |
_didIteratorError2 = false; | |
_iteratorError2 = undefined; | |
context$1$0.prev = 5; | |
_iterator2 = (_temporalAssertDefined(promiseIterator, "promiseIterator", _temporalUndefined) && promiseIterator)[Symbol.iterator](); | |
case 7: | |
if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) { | |
context$1$0.next = 22; | |
break; | |
} | |
promise = _step2.value; | |
context$1$0.prev = 9; | |
context$1$0.next = 12; | |
return regeneratorRuntime.awrap(promise); | |
case 12: | |
eve = context$1$0.sent; | |
console.log("クリックしたでw(時間:" + String(eve.timeStamp) + ")"); | |
context$1$0.next = 19; | |
break; | |
case 16: | |
context$1$0.prev = 16; | |
context$1$0.t0 = context$1$0["catch"](9); | |
console.log("しんだでw(死因:" + String(context$1$0.t0.message) + ")"); | |
case 19: | |
_iteratorNormalCompletion2 = true; | |
context$1$0.next = 7; | |
break; | |
case 22: | |
context$1$0.next = 28; | |
break; | |
case 24: | |
context$1$0.prev = 24; | |
context$1$0.t1 = context$1$0["catch"](5); | |
_didIteratorError2 = true; | |
_iteratorError2 = context$1$0.t1; | |
case 28: | |
context$1$0.prev = 28; | |
context$1$0.prev = 29; | |
if (!_iteratorNormalCompletion2 && _iterator2["return"]) { | |
_iterator2["return"](); | |
} | |
case 31: | |
context$1$0.prev = 31; | |
if (!_didIteratorError2) { | |
context$1$0.next = 34; | |
break; | |
} | |
throw _iteratorError2; | |
case 34: | |
return context$1$0.finish(31); | |
case 35: | |
return context$1$0.finish(28); | |
case 36: | |
case "end": | |
return context$1$0.stop(); | |
} | |
}, null, this, [[5, 24, 28, 36], [9, 16], [29,, 31, 35]]); | |
})(); | |
// こっち(左側)のtextareaのカーソル位置を動かすと(スタイルが変わるので) | |
// それを監視するMutationObserverの例 | |
(function callee$0$0() { | |
var $textarea = _temporalUndefined; | |
var promiseIterator = _temporalUndefined; | |
var _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, promise, data, style; | |
return regeneratorRuntime.async(function callee$0$0$(context$1$0) { | |
while (1) switch (context$1$0.prev = context$1$0.next) { | |
case 0: | |
$textarea = document.querySelector("textarea.ace_text-input"); | |
promiseIterator = (_temporalAssertDefined($textarea, "$textarea", _temporalUndefined) && $textarea)[_temporalAssertDefined(_, "_", _temporalUndefined) && _].promiseObserver({ attributes: true, attributeFilter: ["style"] }, regeneratorRuntime.mark(function callee$1$0(data) { | |
var i; | |
return regeneratorRuntime.wrap(function callee$1$0$(context$2$0) { | |
while (1) switch (context$2$0.prev = context$2$0.next) { | |
case 0: | |
i = 0; | |
case 1: | |
if (!(i < data.length)) { | |
context$2$0.next = 7; | |
break; | |
} | |
context$2$0.next = 4; | |
return data[i]; | |
case 4: | |
++i; | |
context$2$0.next = 1; | |
break; | |
case 7: | |
case "end": | |
return context$2$0.stop(); | |
} | |
}, callee$1$0, this); | |
})); | |
_iteratorNormalCompletion3 = true; | |
_didIteratorError3 = false; | |
_iteratorError3 = undefined; | |
context$1$0.prev = 5; | |
_iterator3 = (_temporalAssertDefined(promiseIterator, "promiseIterator", _temporalUndefined) && promiseIterator)[Symbol.iterator](); | |
case 7: | |
if (_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done) { | |
context$1$0.next = 23; | |
break; | |
} | |
promise = _step3.value; | |
context$1$0.prev = 9; | |
context$1$0.next = 12; | |
return regeneratorRuntime.awrap(promise); | |
case 12: | |
data = context$1$0.sent; | |
style = Array.prototype.slice.call((_temporalAssertDefined($textarea, "$textarea", _temporalUndefined) && $textarea).style).map(function (x) { | |
return String(x) + ":" + String((_temporalAssertDefined($textarea, "$textarea", _temporalUndefined) && $textarea).style[x]); | |
}); | |
console.log("属性変わったでw"); | |
context$1$0.next = 20; | |
break; | |
case 17: | |
context$1$0.prev = 17; | |
context$1$0.t0 = context$1$0["catch"](9); | |
console.log(context$1$0.t0.message); | |
case 20: | |
_iteratorNormalCompletion3 = true; | |
context$1$0.next = 7; | |
break; | |
case 23: | |
context$1$0.next = 29; | |
break; | |
case 25: | |
context$1$0.prev = 25; | |
context$1$0.t1 = context$1$0["catch"](5); | |
_didIteratorError3 = true; | |
_iteratorError3 = context$1$0.t1; | |
case 29: | |
context$1$0.prev = 29; | |
context$1$0.prev = 30; | |
if (!_iteratorNormalCompletion3 && _iterator3["return"]) { | |
_iterator3["return"](); | |
} | |
case 32: | |
context$1$0.prev = 32; | |
if (!_didIteratorError3) { | |
context$1$0.next = 35; | |
break; | |
} | |
throw _iteratorError3; | |
case 35: | |
return context$1$0.finish(32); | |
case 36: | |
return context$1$0.finish(29); | |
case 37: | |
case "end": | |
return context$1$0.stop(); | |
} | |
}, null, this, [[5, 25, 29, 37], [9, 17], [30,, 32, 36]]); | |
})(); | |
// こんな風にイベントで得られるデータ配列をさらにyield可能…! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment