Pārlūkot izejas kodu

feat: new fn getListLength() & add AbortSignal param to pauseFor()

Sv443 2 mēneši atpakaļ
vecāks
revīzija
c9b13d8
5 mainītis faili ar 106 papildinājumiem un 26 dzēšanām
  1. 5 0
      .changeset/proud-pants-care.md
  2. 5 0
      .changeset/swift-carrots-relax.md
  3. 1 0
      README.md
  4. 57 9
      docs.md
  5. 38 17
      lib/misc.ts

+ 5 - 0
.changeset/proud-pants-care.md

@@ -0,0 +1,5 @@
+---
+"@sv443-network/userutils": minor
+---
+
+Added `signal: AbortSignal` and `rejectOnAbort: boolean` params to `pauseFor()` to allow for cutting the pause short

+ 5 - 0
.changeset/swift-carrots-relax.md

@@ -0,0 +1,5 @@
+---
+"@sv443-network/userutils": minor
+---
+
+Added `getListLength()` function to resolve a value of the new `ListWithLength` type

+ 1 - 0
README.md

@@ -71,6 +71,7 @@ View the documentation of previous major releases:
     - [`randomId()`](./docs.md#randomid) - generate a random ID of a given length and radix
     - [`consumeGen()`](./docs.md#consumegen) - consumes a ValueGen and returns the value
     - [`consumeStringGen()`](./docs.md#consumestringgen) - consumes a StringGen and returns the string
+    - [`getListLength()`](./docs.md#getlistlength) - get the length of any object with a numeric `length`, `count` or `size` property
   - [**Arrays:**](./docs.md#arrays)
     - [`randomItem()`](./docs.md#randomitem) - returns a random item from an array
     - [`randomItemIndex()`](./docs.md#randomitemindex) - returns a tuple of a random item and its index from an array

+ 57 - 9
docs.md

@@ -60,6 +60,7 @@ For submitting bug reports or feature requests, please use the [GitHub issue tra
     - [`randomId()`](#randomid) - generate a random ID of a given length and radix
     - [`consumeGen()`](#consumegen) - consumes a ValueGen and returns the value
     - [`consumeStringGen()`](#consumestringgen) - consumes a StringGen and returns the string
+    - [`getListLength()`](#getlistlength) - get the length of any object with a numeric `length`, `count` or `size` property
   - [**Arrays:**](#arrays)
     - [`randomItem()`](#randomitem) - returns a random item from an array
     - [`randomItemIndex()`](#randomitemindex) - returns a tuple of a random item and its index from an array
@@ -1984,10 +1985,12 @@ autoPlural("apple", 2, "-ies");  // "applies"
 ### pauseFor()
 Signature:  
 ```ts
-pauseFor(ms: number): Promise<void>
+pauseFor(time: number, abortSignal?: AbortSignal, rejectOnAbort?: boolean): Promise<void>
 ```
   
 Pauses async execution for a given amount of time.  
+If an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) is passed, the pause will be cut short when the signal is aborted.  
+By default, this will resolve the promise, but you can set `rejectOnAbort` to true to reject it instead.  
   
 <details><summary><b>Example - click to view</b></summary>
 
@@ -1996,8 +1999,20 @@ import { pauseFor } from "@sv443-network/userutils";
 
 async function run() {
   console.log("Hello");
+
   await pauseFor(3000); // waits for 3 seconds
+
   console.log("World");
+
+
+  // can also be cut short manually:
+
+  const controller = new AbortController();
+  setTimeout(() => controller.abort(), 1000);
+
+  await pauseFor(2_147_483_647, controller.signal); // (maximum possible timeout)
+
+  console.log("This gets printed after just 1 second");
 }
 ```
 </details>
@@ -2022,26 +2037,26 @@ Pass an [AbortController's signal](https://developer.mozilla.org/en-US/docs/Web/
 ```ts
 import { fetchAdvanced } from "@sv443-network/userutils";
 
-const { signal, abort } = new AbortController();
+const controller = new AbortController();
 
-fetchAdvanced("https://jokeapi.dev/joke/Any?safe-mode", {
+fetchAdvanced("https://jokeapi.dev/joke/Any?safe-mode&format=json", {
   // times out after 5 seconds:
   timeout: 5000,
   // also accepts any other fetch options like headers and signal:
   headers: {
-    "Accept": "text/plain",
+    "Accept": "application/json",
   },
   // makes the request abortable:
-  signal,
+  signal: controller.signal,
 }).then(async (response) => {
-  console.log("Fetch data:", await response.text());
+  console.log("Fetch data:", await response.json());
 }).catch((err) => {
   console.error("Fetch error:", err);
 });
 
 // can also be aborted manually before the timeout is reached:
 document.querySelector("button#cancel")?.addEventListener("click", () => {
-  abort();
+  controller.abort();
 });
 ```
 </details>
@@ -2234,7 +2249,7 @@ benchmark(true, true);   // Generated 10k in 1054ms
 ```
 </details>
 
-<br><br>
+<br>
 
 ### consumeGen()
 Signature:  
@@ -2268,7 +2283,7 @@ doSomething("foo");
 
 </details>
 
-<br><br>
+<br>
 
 ### consumeStringGen()
 Signature:  
@@ -2313,6 +2328,39 @@ new MyTextPromptThing(420);
 
 </details>
 
+<br>
+
+### getListLength()
+Signature:  
+```ts
+getListLength(obj: ListWithLength, zeroOnInvalid?: boolean): number
+```
+  
+Returns the length of the given list-like object (anything with a numeric `length`, `size` or `count` property, like an array, Map or NodeList).  
+Refer to [the ListWithLength type](#listwithlength) for more info.  
+  
+If the object doesn't have any of these properties, it will return 0 by default.  
+Set `zeroOnInvalid` to false to return NaN instead of 0 if the object doesn't have any of the properties.  
+  
+<details><summary><b>Example - click to view</b></summary>
+
+```ts
+import { getListLength } from "@sv443-network/userutils";
+
+getListLength([1, 2, 3]); // 3
+getListLength("Hello, World!"); // 13
+getListLength(document.querySelectorAll("body")); // 1
+getListLength(new Map([["foo", "bar"], ["baz", "qux"]])); // 2
+getListLength({ size: 42 }); // 42
+
+// returns 0 by default:
+getListLength({ foo: "bar" }); // 0
+
+// can return NaN instead:
+getListLength({ foo: "bar" }, false); // NaN
+```
+</details>
+
 <br><br>
 
 <!-- #region Arrays -->

+ 38 - 17
lib/misc.ts

@@ -9,7 +9,7 @@ import type { ListWithLength, Prettify, Stringifiable } from "./types.js";
 export type PluralType = "auto" | "-s" | "-ies";
 
 /**
- * Automatically pluralizes the given string an `-s` or `-ies` to the passed {@linkcode term}, if {@linkcode num} is not equal to 1.  
+ * Automatically pluralizes the given string by adding an `-s` or `-ies` to the passed {@linkcode term}, if {@linkcode num} is not equal to 1.  
  * By default, words ending in `-y` will have it replaced with `-ies`, and all other words will simply have `-s` appended.  
  * {@linkcode pluralType} will default to `auto` if invalid and {@linkcode num} is set to 2 if it resolves to `NaN`.
  * @param term The term, written in singular form, to auto-convert to plural form
@@ -17,20 +17,15 @@ export type PluralType = "auto" | "-s" | "-ies";
  * @param pluralType Which plural form to use when auto-pluralizing. Defaults to `"auto"`, which removes the last char and uses `-ies` for words ending in `y` and simply appends `-s` for all other words
  */
 export function autoPlural(term: Stringifiable, num: number | ListWithLength, pluralType: PluralType = "auto"): string {
-  if(typeof num !== "number") {
-    if("length" in num)
-      num = num.length;
-    else if("size" in num)
-      num = num.size;
-    else if("count" in num)
-      num = num.count;
-  }
+  let n = num;
+  if(typeof n !== "number")
+    n = getListLength(n, false);
 
   if(!["-s", "-ies"].includes(pluralType))
     pluralType = "auto";
 
-  if(isNaN(num))
-    num = 2;
+  if(isNaN(n))
+    n = 2;
 
   const pType: Exclude<PluralType, "auto"> = pluralType === "auto"
     ? String(term).endsWith("y") ? "-ies" : "-s"
@@ -38,9 +33,9 @@ export function autoPlural(term: Stringifiable, num: number | ListWithLength, pl
 
   switch(pType) {
   case "-s":
-    return `${term}${num === 1 ? "" : "s"}`;
+    return `${term}${n === 1 ? "" : "s"}`;
   case "-ies":
-    return `${String(term).slice(0, -1)}${num === 1 ? "y" : "ies"}`;
+    return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
   default:
     return String(term);
   }
@@ -59,10 +54,18 @@ export function insertValues(input: string, ...values: Stringifiable[]): string
   });
 }
 
-/** Pauses async execution for the specified time in ms */
-export function pauseFor(time: number): Promise<void> {
-  return new Promise<void>((res) => {
-    setTimeout(() => res(), time);
+/**
+ * Pauses async execution for the specified time in ms.  
+ * If an `AbortSignal` is passed, the pause will be aborted when the signal is triggered.  
+ * By default, this will resolve the promise, but you can set {@linkcode rejectOnAbort} to true to reject it instead.
+ */
+export function pauseFor(time: number, signal?: AbortSignal, rejectOnAbort = false): Promise<void> {
+  return new Promise<void>((res, rej) => {
+    const timeout = setTimeout(() => res(), time);
+    signal?.addEventListener("abort", () => {
+      clearTimeout(timeout);
+      rejectOnAbort ? rej(new Error("The pause was aborted")) : res();
+    });
   });
 }
 
@@ -144,3 +147,21 @@ export async function consumeStringGen<TStrUnion extends string>(strGen: StringG
       )
   ) as TStrUnion;
 }
+
+/**
+ * Returns the length of the given list-like object (anything with a numeric `length`, `size` or `count` property, like an array, Map or NodeList).  
+ * If the object doesn't have any of these properties, it will return 0 by default.  
+ * Set {@linkcode zeroOnInvalid} to false to return NaN instead of 0 if the object doesn't have any of the properties.
+ */
+export function getListLength(obj: ListWithLength, zeroOnInvalid = true): number {
+  // will I go to ternary hell for this?
+  return "length" in obj
+    ? obj.length
+    : "size" in obj
+      ? obj.size
+      : "count" in obj
+        ? obj.count
+        : zeroOnInvalid
+          ? 0
+          : NaN;
+}