ETA 24 квітня 17
Я хотів трохи спростити це з деякими async
/await
магіями, оскільки це робить набагато більш лаконічним:
Використовуючи ті ж самі обіцяні-спостерігаючі:
const startObservable = (domNode) => {
var targetNode = domNode;
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
return new Promise((resolve) => {
var observer = new MutationObserver(function (mutations) {
// For the sake of...observation...let's output the mutation to console to see how this all works
mutations.forEach(function (mutation) {
console.log(mutation.type);
});
resolve(mutations)
});
observer.observe(targetNode, observerConfig);
})
}
Ваша функція дзвінка може бути такою ж простою, як:
const waitForMutation = async () => {
const button = document.querySelector('.some-button')
if (button !== null) button.click()
try {
const results = await startObservable(someDomNode)
return results
} catch (err) {
console.error(err)
}
}
Якщо ви хочете додати тайм-аут, ви можете використовувати простий Promise.race
шаблон, як показано тут :
const waitForMutation = async (timeout = 5000 /*in ms*/) => {
const button = document.querySelector('.some-button')
if (button !== null) button.click()
try {
const results = await Promise.race([
startObservable(someDomNode),
// this will throw after the timeout, skipping
// the return & going to the catch block
new Promise((resolve, reject) => setTimeout(
reject,
timeout,
new Error('timed out waiting for mutation')
)
])
return results
} catch (err) {
console.error(err)
}
}
Оригінал
Ви можете це зробити без бібліотек, але вам доведеться використовувати деякі матеріали ES6, тому знайте про проблеми сумісності (тобто якщо ваша аудиторія - це переважно аміші, луддити або, що ще гірше, користувачі IE8)
Спочатку ми будемо використовувати API MutationObserver для побудови об’єкта спостерігача. Ми обернемо цей об’єкт обіцяно, і resolve()
коли зворотний виклик буде знято (ч / т davidwalshblog) стаття в блозі про David Walsh про мутації :
const startObservable = (domNode) => {
var targetNode = domNode;
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
return new Promise((resolve) => {
var observer = new MutationObserver(function (mutations) {
// For the sake of...observation...let's output the mutation to console to see how this all works
mutations.forEach(function (mutation) {
console.log(mutation.type);
});
resolve(mutations)
});
observer.observe(targetNode, observerConfig);
})
}
Тоді ми створимо generator function
. Якщо ви їх ще не використовували, то ви пропускаєте - але короткий конспект такий: він працює як функція синхронізації, і коли він знаходить yield <Promise>
вираз, він чекає неблокуючим способом обіцянки бути виконано ( Генератори роблять більше, ніж це, але це нас цікавить тут ).
// we'll declare our DOM node here, too
let targ = document.querySelector('#domNodeToWatch')
function* getMutation() {
console.log("Starting")
var mutations = yield startObservable(targ)
console.log("done")
}
Хитра частина щодо генераторів - це те, що вони не «повертаються» як нормальна функція. Отже, ми будемо використовувати допоміжну функцію, щоб мати можливість використовувати генератор як звичайну функцію. (знову ж, год / т до відходу )
function runGenerator(g) {
var it = g(), ret;
// asynchronously iterate over generator
(function iterate(val){
ret = it.next( val );
if (!ret.done) {
// poor man's "is it a promise?" test
if ("then" in ret.value) {
// wait on the promise
ret.value.then( iterate );
}
// immediate value: just send right back in
else {
// avoid synchronous recursion
setTimeout( function(){
iterate( ret.value );
}, 0 );
}
}
})();
}
Потім у будь-який момент, перш ніж може статися очікувана мутація DOM, просто запустіть runGenerator(getMutation)
.
Тепер ви можете інтегрувати мутації DOM у керуючий потік синхронного стилю. Як це?