@schukai/monster
Version:
Monster is a simple library for creating fast, robust and lightweight websites.
164 lines (147 loc) • 3.65 kB
JavaScript
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import { Base } from "./base.mjs";
import { isObject } from "./is.mjs";
import { TokenList } from "./tokenlist.mjs";
import { UniqueQueue } from "./uniquequeue.mjs";
import { instanceSymbol } from "../constants.mjs";
export { Observer };
/**
* An observer manages a callback function
*
* The update method is called with the subject object as this pointer. For this reason
* the callback should not be an arrow function, because it gets this pointer of its own context.
*
* Include this class in your project with the following code:
*
* ```js
* import { Observer } from "@schukai/monster/source/types/observer.mjs";
* ```
*
* The callback function is passed as the first argument to the constructor.
*
* ```js
* new Observer(()=>{
* // this is not subject
* })
*
* new Observer(function() {
* // this is subject
* })
* ```
*
* Additional arguments can be passed to the callback. To do this, simply specify them.
*
* ```js
* Observer(function(a, b, c) {
* console.log(a, b, c); // ↦ "a", 2, true
* }, "a", 2, true)
* ```
*
* The callback function must have as many parameters as arguments are given.
*
* @license AGPLv3
* @since 1.0.0
*/
class Observer extends Base {
/**
*
* @param {function} callback
* @param {*} args
*/
constructor(callback, ...args) {
super();
if (typeof callback !== "function") {
throw new Error("observer callback must be a function");
}
this.callback = callback;
this.arguments = args;
this.tags = new TokenList();
this.queue = new UniqueQueue();
}
/**
* This method is called by the `instanceof` operator.
* @return {symbol}
* @since 2.1.0
*/
static get [instanceSymbol]() {
return Symbol.for("@schukai/monster/types/observer");
}
/**
*
* @param {string} tag
* @return {Observer}
*/
addTag(tag) {
this.tags.add(tag);
return this;
}
/**
*
* @param {string} tag
* @return {Observer}
*/
removeTag(tag) {
this.tags.remove(tag);
return this;
}
/**
*
* @return {Array}
*/
getTags() {
return this.tags.entries();
}
/**
*
* @param {string} tag
* @return {boolean}
*/
hasTag(tag) {
return this.tags.contains(tag);
}
/**
*
* @param {object} subject
* @return {Promise}
*/
update(subject) {
const self = this;
if (!isObject(subject)) {
return Promise.reject("subject must be an object");
}
return new Promise(function (resolve, reject) {
self.queue.add(subject);
queueMicrotask(() => {
try {
// the queue and the `queueMicrotask` ensure that an object is not
// informed of the same change more than once.
if (self.queue.isEmpty()) {
resolve();
return;
}
const s = self.queue.poll();
const result = self.callback.apply(s, self.arguments);
if (isObject(result) && result instanceof Promise) {
result.then(resolve).catch(reject);
return;
}
resolve(result);
} catch (e) {
reject(e);
}
});
});
}
}