用 async/await 重写 MutationObserver()
Rewrite MutationObserver() with async/await
如何使用 async/await 编写此突变观察器代码?
我想 return true
在 console.log("Button is appearing...");
之后。有人可以告诉我编写此代码的最佳方法吗?
我还需要澄清一下,这段代码正在监视一个按钮,它出现然后消失。然后再次出现,多次。
因此 mutationObserver
正在观察按钮是否出现多次。不止一次。
var target = document.querySelector('[search-model="SearchPodModel"]')
var observer = new MutationObserver(mutate);
function mutate(mutations) {
for (let i = 0; i < mutations.length; i++) {
if (mutations[i].oldValue === "ej-button rounded-corners arrow-button search-submit holiday-search ng-hide") {
console.log("Button is appearing...");
return true;
};
};
};
var config = { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true };
observer.observe(target, config);
前言:我强烈建议不要依赖突变观察者来观察按钮的class属性是否发生变化。这是不得已才要做的事情。寻找任何其他你可以挂钩的东西,这就是使按钮 appear/disappear 挂钩的原因。
但是回答你的问题:
由于您想要重复通知,因此 promises(因此 async
/await
)不是正确的模型。承诺只结算一次
它没有内置 JavaScript,但您想要的通常称为 observable,它具有(通常)subscribe
和 unsubscribe
方法。这是一个 really observable 的基本的、天真的实现(使用现代 JavaScript;运行 通过 Babel 或类似的如果你需要支持旧环境),但是你可能想去寻找一个库(例如 Rx.js——不是认可,我没有使用过它,只是我碰巧知道的一个例子)具有更丰富的功能,你知道,测试:
class Observable {
// Constructs the observable
constructor(setup) {
// Call the observable executor function, give it the function to call with
// notifications.
setup((spent, value) => {
// Do the notifications
this.#notifyObservers(spent, value);
if (spent) {
// Got a notification that the observable thing is completely done and
// won't be providing any more updates. Release the observers.
this.#observers = null;
}
});
}
// The observers
#observers = new Set();
// Notify observers
#notifyObservers(spent, value) {
// Grab the current list to notify
const observers = new Set(this.#observers);
for (const observer of observers) {
try { observer(spent, value); } catch { }
}
}
// Add an observer. Returns a true if the subscription was successful, false otherwise.
// You can't subscribe to a spent observable, and you can't subscribe twice.
subscribe(observer) {
if (typeof observer !== "function") {
throw new Error("The observer must be a function");
}
if (this.#observers.has(observer) || !this.#observers) {
return false;
}
this.#observers.add(observer);
return true;
}
// Remove an observer. Returns true if the unsubscription was successful, false otherwise.
unsubscribe(observer) {
return this.#observers ? this.#observers.delete(observer) : false;
}
}
然后您可以为这个突变创建一个可观察对象:
// Create an observable for the button
const buttonAppearedObservable = new Observable(notify => {
const target = document.querySelector('[search-model="SearchPodModel"]');
const observer = new MutationObserver(mutate);
function mutate(mutations) {
for (const mutation of mutations) {
if (mutation.oldValue === "ej-button rounded-corners arrow-button search-submit holiday-search ng-hide") {
// Notify observers. The first argument is `false` because this observable isn't "spent" (it may still
// send more notifications). If you wanted to pass a value, you'd pass a second argument.
notify(
false, // This observable isn't "spent"
mutation.target // Pass along the mutation target element (presumably the button?)
);
};
};
};
// Set up the observer
const config = { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true };
observer.observe(target, config);
});
一旦你设置了那个 observable,你就可以订阅它:
buttonAppearedObservable.subscribe((spent, button) => {
if (spent) {
// This is a notification that the button appeared event will never happen again
}
if (button) {
// The button appeared!
console.log(`Button "${button.value}" appeared!`);
}
});
现场示例:
class Observable {
// Constructs the observable
constructor(setup) {
// Call the observable executor function, give it the function to call with
// notifications.
setup((spent, value) => {
// Do the notifications
this.#notifyObservers(spent, value);
if (spent) {
// Got a notification that the observable thing is completely done and
// won't be providing any more updates. Release the observers.
this.#observers = null;
}
});
}
// The observers
#observers = new Set();
// Notify observers
#notifyObservers(spent, value) {
// Grab the current list to notify
const observers = new Set(this.#observers);
for (const observer of observers) {
try { observer(spent, value); } catch { }
}
}
// Add an observer. Returns a true if the subscription was successful, false otherwise.
// You can't subscribe to a spent observable, and you can't subscribe twice.
subscribe(observer) {
if (typeof observer !== "function") {
throw new Error("The observer must be a function");
}
if (this.#observers.has(observer) || !this.#observers) {
return false;
}
this.#observers.add(observer);
return true;
}
// Remove an observer. Returns true if the unsubscription was successful, false otherwise.
unsubscribe(observer) {
return this.#observers ? this.#observers.delete(observer) : false;
}
}
// Create an observable for the button
const buttonAppearedObservable = new Observable(notify => {
const target = document.querySelector('[search-model="SearchPodModel"]');
const observer = new MutationObserver(mutate);
function mutate(mutations) {
for (const mutation of mutations) {
if (mutation.oldValue === "ej-button rounded-corners arrow-button search-submit holiday-search ng-hide") {
// Notify observers. The first argument is `false` because this observable isn't "spent" (it may still
// send more notifications). If you wanted to pass a value, you'd pass a second argument.
notify(
false, // This observable isn't "spent"
mutation.target // Pass along the mutation target element (presumably the button?)
);
};
};
};
// Set up the observer
const config = { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true };
observer.observe(target, config);
});
buttonAppearedObservable.subscribe((spent, button) => {
if (spent) {
// This is a notification that the button appeared event will never happen again
}
if (button) {
// The button appeared!
console.log(`Button "${button.value}" appeared!`);
}
});
// Stand-in code to make a button appear/disappear every second
let counter = 0;
let button = document.querySelector(`[search-model="SearchPodModel"] input[type=button]`);
let timer = setInterval(() => {
if (button.classList.contains("ng-hide")) {
++counter;
} else if (counter >= 10) {
console.log("Stopping the timer");
clearInterval(timer);
timer = 0;
return;
}
button.value = `Button ${counter}`;
button.classList.toggle("ng-hide");
}, 500);
.ng-hide {
display: none;
}
<!-- NOTE: `search-model` isnt' a valid attribute for any DOM element. Use the data-* prefix for custom attributes -->
<div search-model="SearchPodModel">
<input type="button" class="ej-button rounded-corners arrow-button search-submit holiday-search ng-hide" value="The Button">
</div>
所有这些都是非常即兴的。同样,您可能会寻找强大的库等。
如何使用 async/await 编写此突变观察器代码?
我想 return true
在 console.log("Button is appearing...");
之后。有人可以告诉我编写此代码的最佳方法吗?
我还需要澄清一下,这段代码正在监视一个按钮,它出现然后消失。然后再次出现,多次。
因此 mutationObserver
正在观察按钮是否出现多次。不止一次。
var target = document.querySelector('[search-model="SearchPodModel"]')
var observer = new MutationObserver(mutate);
function mutate(mutations) {
for (let i = 0; i < mutations.length; i++) {
if (mutations[i].oldValue === "ej-button rounded-corners arrow-button search-submit holiday-search ng-hide") {
console.log("Button is appearing...");
return true;
};
};
};
var config = { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true };
observer.observe(target, config);
前言:我强烈建议不要依赖突变观察者来观察按钮的class属性是否发生变化。这是不得已才要做的事情。寻找任何其他你可以挂钩的东西,这就是使按钮 appear/disappear 挂钩的原因。
但是回答你的问题:
由于您想要重复通知,因此 promises(因此 async
/await
)不是正确的模型。承诺只结算一次
它没有内置 JavaScript,但您想要的通常称为 observable,它具有(通常)subscribe
和 unsubscribe
方法。这是一个 really observable 的基本的、天真的实现(使用现代 JavaScript;运行 通过 Babel 或类似的如果你需要支持旧环境),但是你可能想去寻找一个库(例如 Rx.js——不是认可,我没有使用过它,只是我碰巧知道的一个例子)具有更丰富的功能,你知道,测试:
class Observable {
// Constructs the observable
constructor(setup) {
// Call the observable executor function, give it the function to call with
// notifications.
setup((spent, value) => {
// Do the notifications
this.#notifyObservers(spent, value);
if (spent) {
// Got a notification that the observable thing is completely done and
// won't be providing any more updates. Release the observers.
this.#observers = null;
}
});
}
// The observers
#observers = new Set();
// Notify observers
#notifyObservers(spent, value) {
// Grab the current list to notify
const observers = new Set(this.#observers);
for (const observer of observers) {
try { observer(spent, value); } catch { }
}
}
// Add an observer. Returns a true if the subscription was successful, false otherwise.
// You can't subscribe to a spent observable, and you can't subscribe twice.
subscribe(observer) {
if (typeof observer !== "function") {
throw new Error("The observer must be a function");
}
if (this.#observers.has(observer) || !this.#observers) {
return false;
}
this.#observers.add(observer);
return true;
}
// Remove an observer. Returns true if the unsubscription was successful, false otherwise.
unsubscribe(observer) {
return this.#observers ? this.#observers.delete(observer) : false;
}
}
然后您可以为这个突变创建一个可观察对象:
// Create an observable for the button
const buttonAppearedObservable = new Observable(notify => {
const target = document.querySelector('[search-model="SearchPodModel"]');
const observer = new MutationObserver(mutate);
function mutate(mutations) {
for (const mutation of mutations) {
if (mutation.oldValue === "ej-button rounded-corners arrow-button search-submit holiday-search ng-hide") {
// Notify observers. The first argument is `false` because this observable isn't "spent" (it may still
// send more notifications). If you wanted to pass a value, you'd pass a second argument.
notify(
false, // This observable isn't "spent"
mutation.target // Pass along the mutation target element (presumably the button?)
);
};
};
};
// Set up the observer
const config = { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true };
observer.observe(target, config);
});
一旦你设置了那个 observable,你就可以订阅它:
buttonAppearedObservable.subscribe((spent, button) => {
if (spent) {
// This is a notification that the button appeared event will never happen again
}
if (button) {
// The button appeared!
console.log(`Button "${button.value}" appeared!`);
}
});
现场示例:
class Observable {
// Constructs the observable
constructor(setup) {
// Call the observable executor function, give it the function to call with
// notifications.
setup((spent, value) => {
// Do the notifications
this.#notifyObservers(spent, value);
if (spent) {
// Got a notification that the observable thing is completely done and
// won't be providing any more updates. Release the observers.
this.#observers = null;
}
});
}
// The observers
#observers = new Set();
// Notify observers
#notifyObservers(spent, value) {
// Grab the current list to notify
const observers = new Set(this.#observers);
for (const observer of observers) {
try { observer(spent, value); } catch { }
}
}
// Add an observer. Returns a true if the subscription was successful, false otherwise.
// You can't subscribe to a spent observable, and you can't subscribe twice.
subscribe(observer) {
if (typeof observer !== "function") {
throw new Error("The observer must be a function");
}
if (this.#observers.has(observer) || !this.#observers) {
return false;
}
this.#observers.add(observer);
return true;
}
// Remove an observer. Returns true if the unsubscription was successful, false otherwise.
unsubscribe(observer) {
return this.#observers ? this.#observers.delete(observer) : false;
}
}
// Create an observable for the button
const buttonAppearedObservable = new Observable(notify => {
const target = document.querySelector('[search-model="SearchPodModel"]');
const observer = new MutationObserver(mutate);
function mutate(mutations) {
for (const mutation of mutations) {
if (mutation.oldValue === "ej-button rounded-corners arrow-button search-submit holiday-search ng-hide") {
// Notify observers. The first argument is `false` because this observable isn't "spent" (it may still
// send more notifications). If you wanted to pass a value, you'd pass a second argument.
notify(
false, // This observable isn't "spent"
mutation.target // Pass along the mutation target element (presumably the button?)
);
};
};
};
// Set up the observer
const config = { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true };
observer.observe(target, config);
});
buttonAppearedObservable.subscribe((spent, button) => {
if (spent) {
// This is a notification that the button appeared event will never happen again
}
if (button) {
// The button appeared!
console.log(`Button "${button.value}" appeared!`);
}
});
// Stand-in code to make a button appear/disappear every second
let counter = 0;
let button = document.querySelector(`[search-model="SearchPodModel"] input[type=button]`);
let timer = setInterval(() => {
if (button.classList.contains("ng-hide")) {
++counter;
} else if (counter >= 10) {
console.log("Stopping the timer");
clearInterval(timer);
timer = 0;
return;
}
button.value = `Button ${counter}`;
button.classList.toggle("ng-hide");
}, 500);
.ng-hide {
display: none;
}
<!-- NOTE: `search-model` isnt' a valid attribute for any DOM element. Use the data-* prefix for custom attributes -->
<div search-model="SearchPodModel">
<input type="button" class="ej-button rounded-corners arrow-button search-submit holiday-search ng-hide" value="The Button">
</div>
所有这些都是非常即兴的。同样,您可能会寻找强大的库等。