ETA 24 apr 17
Volevo semplificare un po 'questo con un po' async
/ await
magia, in quanto lo rende molto più conciso:
Utilizzando lo stesso promisificato-osservabile:
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);
})
}
La tua funzione di chiamata può essere semplice come:
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)
}
}
Se si desidera aggiungere un timeout, è possibile utilizzare un Promise.race
modello semplice , come dimostrato qui :
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)
}
}
Originale
Puoi farlo senza librerie, ma dovresti usare alcune cose ES6, quindi sii consapevole dei problemi di compatibilità (ad esempio, se il tuo pubblico è principalmente Amish, luddite o, peggio, utenti IE8)
Innanzitutto, utilizzeremo l' API MutationObserver per costruire un oggetto osservatore. Avvolgeremo questo oggetto in una promessa, e resolve()
quando viene attivato il callback (h / t davidwalshblog) david walsh articolo del blog sulle mutazioni :
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);
})
}
Quindi, creeremo un generator function
. Se non li hai ancora usati, ti stai perdendo, ma una breve sinossi è: funziona come una funzione di sincronizzazione e quando trova yield <Promise>
un'espressione, attende in modo non bloccante che la promessa sia appagato (i generatori fanno più di questo, ma questo è ciò a cui siamo interessati qui ).
// 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")
}
Una parte difficile dei generatori è che non "ritornano" come una normale funzione. Quindi, useremo una funzione di aiuto per essere in grado di usare il generatore come una normale funzione. (di nuovo, da h / t a dwb )
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 );
}
}
})();
}
Quindi, in qualsiasi momento prima che possa verificarsi la mutazione DOM prevista, esegui semplicemente runGenerator(getMutation)
.
Ora puoi integrare le mutazioni DOM in un flusso di controllo in stile sincrono. Che ne dici.