@ayonli/jsext
Advanced tools
Comparing version 0.5.2 to 0.5.3
25
chan.ts
@@ -28,3 +28,3 @@ export class Channel<T> implements AsyncIterable<T> { | ||
* - If the buffer size is within the capacity, the data will be pushed to the buffer. | ||
* - Otherwise, this function will block until there is new room for the data in the | ||
* - Otherwise, this function will block until there is new space for the data in the | ||
* buffer. | ||
@@ -72,4 +72,11 @@ */ | ||
if (this.buffer.length) { | ||
return Promise.resolve(this.buffer.shift()); | ||
const data = this.buffer.shift(); | ||
if (this.state === 2 && !this.buffer.length) { | ||
this.state = 0; | ||
} | ||
return Promise.resolve(data); | ||
} else if (this.pub) { | ||
this.state === 2 && (this.state = 0); | ||
return Promise.resolve(this.pub()); | ||
@@ -84,5 +91,9 @@ } else if (this.state === 0) { | ||
return Promise.reject(error); | ||
} else if (this.state === 2) { | ||
this.state = 0; | ||
return Promise.resolve(undefined); | ||
} else { | ||
return new Promise<T>((resolve, reject) => { | ||
this.sub = (err: unknown, data?: T) => { | ||
this.state === 2 && (this.state = 0); | ||
this.sub = undefined; | ||
@@ -107,9 +118,3 @@ err ? reject(err) : resolve(data as T); | ||
this.error = err; | ||
// Delay the closure till the next event loop so that `sub` can be bound in the | ||
// `for await...of...` loop and the last message can be delivered. | ||
setTimeout(() => { | ||
this.state = 0; | ||
this.sub?.(err, undefined); | ||
}); | ||
this.sub?.(err, undefined); | ||
} | ||
@@ -141,3 +146,3 @@ | ||
* be queued in the buffer first and then consumed by the receiver in FIFO order. Once the | ||
* buffer size reaches the capacity limit, no more data will be sent unless there is new room | ||
* buffer size reaches the capacity limit, no more data will be sent unless there is new space | ||
* available. | ||
@@ -144,0 +149,0 @@ * |
@@ -24,3 +24,3 @@ 'use strict'; | ||
* - If the buffer size is within the capacity, the data will be pushed to the buffer. | ||
* - Otherwise, this function will block until there is new room for the data in the | ||
* - Otherwise, this function will block until there is new space for the data in the | ||
* buffer. | ||
@@ -71,5 +71,10 @@ */ | ||
if (this.buffer.length) { | ||
return Promise.resolve(this.buffer.shift()); | ||
const data = this.buffer.shift(); | ||
if (this.state === 2 && !this.buffer.length) { | ||
this.state = 0; | ||
} | ||
return Promise.resolve(data); | ||
} | ||
else if (this.pub) { | ||
this.state === 2 && (this.state = 0); | ||
return Promise.resolve(this.pub()); | ||
@@ -87,5 +92,10 @@ } | ||
} | ||
else if (this.state === 2) { | ||
this.state = 0; | ||
return Promise.resolve(undefined); | ||
} | ||
else { | ||
return new Promise((resolve, reject) => { | ||
this.sub = (err, data) => { | ||
this.state === 2 && (this.state = 0); | ||
this.sub = undefined; | ||
@@ -107,11 +117,6 @@ err ? reject(err) : resolve(data); | ||
close(err = null) { | ||
var _a; | ||
this.state = 2; | ||
this.error = err; | ||
// Delay the closure till the next event loop so that `sub` can be bound in the | ||
// `for await...of...` loop and the last message can be delivered. | ||
setTimeout(() => { | ||
var _a; | ||
this.state = 0; | ||
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.call(this, err, undefined); | ||
}); | ||
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.call(this, err, undefined); | ||
} | ||
@@ -141,3 +146,3 @@ [Symbol.asyncIterator]() { | ||
* be queued in the buffer first and then consumed by the receiver in FIFO order. Once the | ||
* buffer size reaches the capacity limit, no more data will be sent unless there is new room | ||
* buffer size reaches the capacity limit, no more data will be sent unless there is new space | ||
* available. | ||
@@ -144,0 +149,0 @@ * |
@@ -10,4 +10,4 @@ 'use strict'; | ||
var mixins = require('./mixins.js'); | ||
var isSubclassOf = require('./isSubclassOf.js'); | ||
var chan = require('./chan.js'); | ||
var queue = require('./queue.js'); | ||
var read = require('./read.js'); | ||
@@ -28,5 +28,7 @@ var run = require('./run.js'); | ||
mixins: mixins.default, | ||
isSubclassOf: isSubclassOf.default, | ||
isSubclassOf: mixins.isSubclassOf, | ||
chan: chan.default, | ||
queue: queue.default, | ||
read: read.default, | ||
readAll: read.readAll, | ||
run: run.default, | ||
@@ -40,7 +42,10 @@ example: example.default, | ||
exports.throttle = throttle.default; | ||
exports.isSubclassOf = mixins.isSubclassOf; | ||
exports.mixins = mixins.default; | ||
exports.isSubclassOf = isSubclassOf.default; | ||
exports.Channel = chan.Channel; | ||
exports.chan = chan.default; | ||
exports.Queue = queue.Queue; | ||
exports.queue = queue.default; | ||
exports.read = read.default; | ||
exports.readAll = read.readAll; | ||
exports.run = run.default; | ||
@@ -47,0 +52,0 @@ exports.example = example.default; |
@@ -71,4 +71,21 @@ 'use strict'; | ||
} | ||
/** | ||
* Checks if a class is a subclass of another class. | ||
* | ||
* @example | ||
* ```ts | ||
* class Moment extends Date {} | ||
* | ||
* console.assert(isSubclassOf(Moment, Date)); | ||
* console.assert(isSubclassOf(Moment, Object)); // all classes are subclasses of Object | ||
* ``` | ||
*/ | ||
function isSubclassOf(ctor1, ctor2) { | ||
return typeof ctor1 === "function" | ||
&& typeof ctor2 === "function" | ||
&& ctor1.prototype instanceof ctor2; | ||
} | ||
exports.default = mixins; | ||
exports.isSubclassOf = isSubclassOf; | ||
//# sourceMappingURL=mixins.js.map |
@@ -38,3 +38,3 @@ 'use strict'; | ||
} | ||
/** Returns a random integer ranged from `min` to `max`. */ | ||
/** Returns a random integer ranged from `min` to `max` (inclusive). */ | ||
function random(min, max) { | ||
@@ -48,3 +48,3 @@ return min + Math.floor(Math.random() * (max - min + 1)); | ||
yield id; | ||
if (id >= max) { | ||
if ((id += step) > max) { | ||
if (loop) { | ||
@@ -57,5 +57,2 @@ id = min; | ||
} | ||
else { | ||
id += step; | ||
} | ||
} | ||
@@ -62,0 +59,0 @@ } |
@@ -32,8 +32,4 @@ 'use strict'; | ||
async function until(test) { | ||
if (typeof globalThis.setImmediate === "undefined") { | ||
// @ts-ignore | ||
globalThis.setImmediate = (cb) => setTimeout(cb, 0); | ||
} | ||
do { | ||
await new Promise(globalThis.setImmediate); | ||
await new Promise(resolve => setTimeout(resolve)); | ||
} while ((await test()) == false); | ||
@@ -40,0 +36,0 @@ } |
@@ -151,4 +151,21 @@ 'use strict'; | ||
} | ||
/** | ||
* Reads all values from the iterable object at once. | ||
* | ||
* @example | ||
* ```ts | ||
* const file = fs.createReadStream("./package.json"); | ||
* const chunks = await readAll(file); | ||
* ``` | ||
*/ | ||
async function readAll(iterable) { | ||
const list = []; | ||
for await (const chunk of iterable) { | ||
list.push(chunk); | ||
} | ||
return list; | ||
} | ||
exports.default = read; | ||
exports.readAll = readAll; | ||
//# sourceMappingURL=read.js.map |
@@ -19,3 +19,3 @@ 'use strict'; | ||
} | ||
/** Returns a random string, the charset matches `/[0-9a-zA-Z]/` */ | ||
/** Returns a random string, the charset matches `/[0-9a-zA-Z]/`. */ | ||
function random(length) { | ||
@@ -22,0 +22,0 @@ const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
@@ -9,5 +9,6 @@ 'use strict'; | ||
const duration = typeof options === "number" ? options : options.duration; | ||
const noWait = typeof options === "number" ? false : !!(options === null || options === void 0 ? void 0 : options.noWait); | ||
const handleCall = function (cache, ...args) { | ||
var _a; | ||
if (cache.result && Date.now() < ((_a = cache.expires) !== null && _a !== void 0 ? _a : 0)) { | ||
if (cache.result && ((cache.pending && noWait) || Date.now() < ((_a = cache.expires) !== null && _a !== void 0 ? _a : 0))) { | ||
if (cache.result.error) { | ||
@@ -20,7 +21,36 @@ throw cache.result.error; | ||
} | ||
else if (cache.pending) { | ||
return cache.pending; | ||
} | ||
try { | ||
const returns = handler.call(this, ...args); | ||
cache.result = { value: returns }; | ||
cache.expires = Date.now() + duration; | ||
return returns; | ||
if (typeof (returns === null || returns === void 0 ? void 0 : returns.then) === "function") { | ||
cache.pending = returns.then(value => { | ||
cache.pending = undefined; | ||
cache.result = { value }; | ||
cache.expires = Date.now() + duration; | ||
return value; | ||
}).catch(error => { | ||
cache.pending = undefined; | ||
cache.result = { error }; | ||
cache.expires = Date.now() + duration; | ||
throw error; | ||
}); | ||
if (noWait && cache.result) { | ||
if (cache.result.error) { | ||
throw cache.result.error; | ||
} | ||
else { | ||
return cache.result.value; | ||
} | ||
} | ||
else { | ||
return cache.pending; | ||
} | ||
} | ||
else { | ||
cache.result = { value: returns }; | ||
cache.expires = Date.now() + duration; | ||
return returns; | ||
} | ||
} | ||
@@ -27,0 +57,0 @@ catch (error) { |
@@ -20,3 +20,3 @@ class Channel { | ||
* - If the buffer size is within the capacity, the data will be pushed to the buffer. | ||
* - Otherwise, this function will block until there is new room for the data in the | ||
* - Otherwise, this function will block until there is new space for the data in the | ||
* buffer. | ||
@@ -67,5 +67,10 @@ */ | ||
if (this.buffer.length) { | ||
return Promise.resolve(this.buffer.shift()); | ||
const data = this.buffer.shift(); | ||
if (this.state === 2 && !this.buffer.length) { | ||
this.state = 0; | ||
} | ||
return Promise.resolve(data); | ||
} | ||
else if (this.pub) { | ||
this.state === 2 && (this.state = 0); | ||
return Promise.resolve(this.pub()); | ||
@@ -83,5 +88,10 @@ } | ||
} | ||
else if (this.state === 2) { | ||
this.state = 0; | ||
return Promise.resolve(undefined); | ||
} | ||
else { | ||
return new Promise((resolve, reject) => { | ||
this.sub = (err, data) => { | ||
this.state === 2 && (this.state = 0); | ||
this.sub = undefined; | ||
@@ -103,11 +113,6 @@ err ? reject(err) : resolve(data); | ||
close(err = null) { | ||
var _a; | ||
this.state = 2; | ||
this.error = err; | ||
// Delay the closure till the next event loop so that `sub` can be bound in the | ||
// `for await...of...` loop and the last message can be delivered. | ||
setTimeout(() => { | ||
var _a; | ||
this.state = 0; | ||
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.call(this, err, undefined); | ||
}); | ||
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.call(this, err, undefined); | ||
} | ||
@@ -137,3 +142,3 @@ [Symbol.asyncIterator]() { | ||
* be queued in the buffer first and then consumed by the receiver in FIFO order. Once the | ||
* buffer size reaches the capacity limit, no more data will be sent unless there is new room | ||
* buffer size reaches the capacity limit, no more data will be sent unless there is new space | ||
* available. | ||
@@ -140,0 +145,0 @@ * |
@@ -5,7 +5,8 @@ import _try from './try.js'; | ||
import throttle from './throttle.js'; | ||
import mixins from './mixins.js'; | ||
import isSubclassOf from './isSubclassOf.js'; | ||
import mixins, { isSubclassOf } from './mixins.js'; | ||
import chan from './chan.js'; | ||
export { Channel } from './chan.js'; | ||
import read from './read.js'; | ||
import queue from './queue.js'; | ||
export { Queue } from './queue.js'; | ||
import read, { readAll } from './read.js'; | ||
import run from './run.js'; | ||
@@ -27,3 +28,5 @@ import example from './example.js'; | ||
chan, | ||
queue, | ||
read, | ||
readAll, | ||
run, | ||
@@ -33,3 +36,3 @@ example, | ||
export { AsyncFunction, AsyncGeneratorFunction, _try, chan, jsext as default, example, func, isSubclassOf, mixins, read, run, throttle, wrap }; | ||
export { AsyncFunction, AsyncGeneratorFunction, _try, chan, jsext as default, example, func, isSubclassOf, mixins, queue, read, readAll, run, throttle, wrap }; | ||
//# sourceMappingURL=index.js.map |
@@ -67,4 +67,20 @@ import { hasOwn } from './object/index.js'; | ||
} | ||
/** | ||
* Checks if a class is a subclass of another class. | ||
* | ||
* @example | ||
* ```ts | ||
* class Moment extends Date {} | ||
* | ||
* console.assert(isSubclassOf(Moment, Date)); | ||
* console.assert(isSubclassOf(Moment, Object)); // all classes are subclasses of Object | ||
* ``` | ||
*/ | ||
function isSubclassOf(ctor1, ctor2) { | ||
return typeof ctor1 === "function" | ||
&& typeof ctor2 === "function" | ||
&& ctor1.prototype instanceof ctor2; | ||
} | ||
export { mixins as default }; | ||
export { mixins as default, isSubclassOf }; | ||
//# sourceMappingURL=mixins.js.map |
@@ -36,3 +36,3 @@ /** Returns `true` if the given value is a float number, `false` otherwise. */ | ||
} | ||
/** Returns a random integer ranged from `min` to `max`. */ | ||
/** Returns a random integer ranged from `min` to `max` (inclusive). */ | ||
function random(min, max) { | ||
@@ -46,3 +46,3 @@ return min + Math.floor(Math.random() * (max - min + 1)); | ||
yield id; | ||
if (id >= max) { | ||
if ((id += step) > max) { | ||
if (loop) { | ||
@@ -55,5 +55,2 @@ id = min; | ||
} | ||
else { | ||
id += step; | ||
} | ||
} | ||
@@ -60,0 +57,0 @@ } |
@@ -30,8 +30,4 @@ /** Try to resolve a promise with a timeout limit. */ | ||
async function until(test) { | ||
if (typeof globalThis.setImmediate === "undefined") { | ||
// @ts-ignore | ||
globalThis.setImmediate = (cb) => setTimeout(cb, 0); | ||
} | ||
do { | ||
await new Promise(globalThis.setImmediate); | ||
await new Promise(resolve => setTimeout(resolve)); | ||
} while ((await test()) == false); | ||
@@ -38,0 +34,0 @@ } |
@@ -147,4 +147,20 @@ import chan from './chan.js'; | ||
} | ||
/** | ||
* Reads all values from the iterable object at once. | ||
* | ||
* @example | ||
* ```ts | ||
* const file = fs.createReadStream("./package.json"); | ||
* const chunks = await readAll(file); | ||
* ``` | ||
*/ | ||
async function readAll(iterable) { | ||
const list = []; | ||
for await (const chunk of iterable) { | ||
list.push(chunk); | ||
} | ||
return list; | ||
} | ||
export { read as default }; | ||
export { read as default, readAll }; | ||
//# sourceMappingURL=read.js.map |
@@ -17,3 +17,3 @@ import { chunk as chunk$1 } from '../array/index.js'; | ||
} | ||
/** Returns a random string, the charset matches `/[0-9a-zA-Z]/` */ | ||
/** Returns a random string, the charset matches `/[0-9a-zA-Z]/`. */ | ||
function random(length) { | ||
@@ -20,0 +20,0 @@ const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
@@ -5,5 +5,6 @@ const throttleCaches = new Map(); | ||
const duration = typeof options === "number" ? options : options.duration; | ||
const noWait = typeof options === "number" ? false : !!(options === null || options === void 0 ? void 0 : options.noWait); | ||
const handleCall = function (cache, ...args) { | ||
var _a; | ||
if (cache.result && Date.now() < ((_a = cache.expires) !== null && _a !== void 0 ? _a : 0)) { | ||
if (cache.result && ((cache.pending && noWait) || Date.now() < ((_a = cache.expires) !== null && _a !== void 0 ? _a : 0))) { | ||
if (cache.result.error) { | ||
@@ -16,7 +17,36 @@ throw cache.result.error; | ||
} | ||
else if (cache.pending) { | ||
return cache.pending; | ||
} | ||
try { | ||
const returns = handler.call(this, ...args); | ||
cache.result = { value: returns }; | ||
cache.expires = Date.now() + duration; | ||
return returns; | ||
if (typeof (returns === null || returns === void 0 ? void 0 : returns.then) === "function") { | ||
cache.pending = returns.then(value => { | ||
cache.pending = undefined; | ||
cache.result = { value }; | ||
cache.expires = Date.now() + duration; | ||
return value; | ||
}).catch(error => { | ||
cache.pending = undefined; | ||
cache.result = { error }; | ||
cache.expires = Date.now() + duration; | ||
throw error; | ||
}); | ||
if (noWait && cache.result) { | ||
if (cache.result.error) { | ||
throw cache.result.error; | ||
} | ||
else { | ||
return cache.result.value; | ||
} | ||
} | ||
else { | ||
return cache.pending; | ||
} | ||
} | ||
else { | ||
cache.result = { value: returns }; | ||
cache.expires = Date.now() + duration; | ||
return returns; | ||
} | ||
} | ||
@@ -23,0 +53,0 @@ catch (error) { |
12
index.ts
@@ -5,10 +5,10 @@ import _try from "./try.ts"; | ||
import throttle from "./throttle.ts"; | ||
import mixins from "./mixins.ts"; | ||
import isSubclassOf from "./isSubclassOf.ts"; | ||
import mixins, { isSubclassOf } from "./mixins.ts"; | ||
import chan, { Channel } from "./chan.ts"; | ||
import read from "./read.ts"; | ||
import queue, { Queue } from "./queue.ts"; | ||
import read, { readAll } from "./read.ts"; | ||
import run from "./run.ts"; | ||
import example from "./example.ts"; | ||
export { Channel }; | ||
export { Channel, Queue }; | ||
export const AsyncFunction = (async function () { }).constructor as AsyncFunctionConstructor; | ||
@@ -56,3 +56,5 @@ export const AsyncGeneratorFunction = (async function* () { }).constructor as AsyncGeneratorFunctionConstructor; | ||
chan, | ||
queue, | ||
read, | ||
readAll, | ||
run, | ||
@@ -71,5 +73,7 @@ example, | ||
chan, | ||
queue, | ||
read, | ||
readAll, | ||
run, | ||
example, | ||
}; |
@@ -117,1 +117,18 @@ import type { Constructor } from "./index.ts"; | ||
} | ||
/** | ||
* Checks if a class is a subclass of another class. | ||
* | ||
* @example | ||
* ```ts | ||
* class Moment extends Date {} | ||
* | ||
* console.assert(isSubclassOf(Moment, Date)); | ||
* console.assert(isSubclassOf(Moment, Object)); // all classes are subclasses of Object | ||
* ``` | ||
*/ | ||
export function isSubclassOf<T, B>(ctor1: Constructor<T>, ctor2: Constructor<B>): boolean { | ||
return typeof ctor1 === "function" | ||
&& typeof ctor2 === "function" | ||
&& ctor1.prototype instanceof ctor2; | ||
} |
@@ -38,3 +38,3 @@ /** Returns `true` if the given value is a float number, `false` otherwise. */ | ||
/** Returns a random integer ranged from `min` to `max`. */ | ||
/** Returns a random integer ranged from `min` to `max` (inclusive). */ | ||
export function random(min: number, max: number): number { | ||
@@ -51,3 +51,3 @@ return min + Math.floor(Math.random() * (max - min + 1)); | ||
if (id >= max) { | ||
if ((id += step) > max) { | ||
if (loop) { | ||
@@ -58,6 +58,4 @@ id = min; | ||
} | ||
} else { | ||
id += step; | ||
} | ||
} | ||
} |
{ | ||
"name": "@ayonli/jsext", | ||
"version": "0.5.2", | ||
"version": "0.5.3", | ||
"description": "Additional functions for JavaScript programming in practice.", | ||
@@ -5,0 +5,0 @@ "exports": { |
@@ -33,8 +33,5 @@ /** Try to resolve a promise with a timeout limit. */ | ||
export async function until(test: () => boolean | Promise<boolean>): Promise<void> { | ||
if (typeof globalThis.setImmediate === "undefined") { | ||
// @ts-ignore | ||
globalThis.setImmediate = (cb: () => void) => setTimeout(cb, 0); | ||
} | ||
do { await new Promise<void>(globalThis.setImmediate); } while ((await test()) == false); | ||
do { | ||
await new Promise<void>(resolve => setTimeout(resolve)); | ||
} while ((await test()) == false); | ||
} |
19
read.ts
@@ -220,1 +220,20 @@ import chan from "./chan.ts"; | ||
} | ||
/** | ||
* Reads all values from the iterable object at once. | ||
* | ||
* @example | ||
* ```ts | ||
* const file = fs.createReadStream("./package.json"); | ||
* const chunks = await readAll(file); | ||
* ``` | ||
*/ | ||
export async function readAll<T>(iterable: AsyncIterable<T>): Promise<T[]> { | ||
const list: T[] = []; | ||
for await (const chunk of iterable) { | ||
list.push(chunk) | ||
} | ||
return list; | ||
} |
@@ -28,3 +28,5 @@ # JsExt | ||
- [jsext.chan](#jsextchan) | ||
- [jsext.queue](#jsextqueue) | ||
- [jsext.read](#jsextread) | ||
- [jsext.readAll](#jsextreadall) | ||
- [jsext.run](#jsextrun) | ||
@@ -260,2 +262,7 @@ - [jsext.example](#jsextexample) | ||
for?: any; | ||
/** | ||
* When turned on, respond with the last cache (if available) immediately, even if it has | ||
* expired, and update the cache in the background. | ||
*/ | ||
noWait?: boolean; | ||
}): Fn; | ||
@@ -266,4 +273,4 @@ ``` | ||
If a subsequent call happens within the `duration`, the previous result will be returned and | ||
the `handler` function will not be invoked. | ||
If a subsequent call happens within the `duration` (in milliseconds), the previous result will | ||
be returned and the `handler` function will not be invoked. | ||
@@ -381,3 +388,3 @@ **Example** | ||
be queued in the buffer first and then consumed by the receiver in FIFO order. Once the | ||
buffer size reaches the capacity limit, no more data will be sent unless there is new room | ||
buffer size reaches the capacity limit, no more data will be sent unless there is new space | ||
available. | ||
@@ -388,4 +395,2 @@ | ||
--- | ||
**Example** | ||
@@ -452,2 +457,41 @@ | ||
--- | ||
### jsext.queue | ||
```ts | ||
function queue<T>(handler: (data: T) => Promise<void>, bufferSize?: number): Queue<T> | ||
``` | ||
Processes data sequentially by the given `handler` function and prevents concurrency | ||
conflicts, it returns a queue instance that we can push data into. | ||
`bufferSize` is the maximum capacity of the underlying channel, once reached, the push | ||
operation will block until there is new space available. Bu default, this option is not set and | ||
use a non-buffered channel instead. | ||
**Example** | ||
```ts | ||
const list: string[] = []; | ||
const q = queue(async (str: string) => { | ||
await Promise.resolve(null); | ||
list.push(str); | ||
}); | ||
q.onError(err => { | ||
console.error(err); | ||
}); | ||
await q.push("foo"); | ||
await q.push("foo"); | ||
console.log(list.length); | ||
q.close(); | ||
// output: | ||
// 2 | ||
``` | ||
--- | ||
### jsext.read | ||
@@ -523,2 +567,19 @@ | ||
### jsext.readAll | ||
```ts | ||
function readAll<T>(iterable: AsyncIterable<T>): Promise<T[]>; | ||
``` | ||
Reads all values from the iterable object at once. | ||
**Example** | ||
```ts | ||
const file = fs.createReadStream("./package.json"); | ||
const chunks = await readAll(file); | ||
``` | ||
--- | ||
### jsext.run | ||
@@ -645,3 +706,4 @@ | ||
- `Channel` | ||
- `Channel<T>` | ||
- `Queue<T>` | ||
- `AsyncFunction` | ||
@@ -656,3 +718,3 @@ - `AsyncGeneratorFunction` | ||
When [augment](./augment.ts)ing, these types will be exposed to the global scope (except for | ||
`Channel`). | ||
`Channel` and `Queue`). | ||
@@ -659,0 +721,0 @@ ## Sub-packages |
@@ -16,3 +16,3 @@ import { chunk as _chunk } from "../array/index.ts"; | ||
/** Returns a random string, the charset matches `/[0-9a-zA-Z]/` */ | ||
/** Returns a random string, the charset matches `/[0-9a-zA-Z]/`. */ | ||
export function random(length: number): string { | ||
@@ -19,0 +19,0 @@ const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
@@ -5,2 +5,3 @@ type ThrottleCache = { | ||
result?: { value?: any; error?: unknown; }; | ||
pending?: Promise<any>; | ||
}; | ||
@@ -12,4 +13,4 @@ const throttleCaches = new Map<any, ThrottleCache>(); | ||
* | ||
* If a subsequent call happens within the `duration`, the previous result will be returned and | ||
* the `handler` function will not be invoked. | ||
* If a subsequent call happens within the `duration` (in milliseconds), the previous result will | ||
* be returned and the `handler` function will not be invoked. | ||
* | ||
@@ -54,2 +55,7 @@ * @example | ||
for?: any; | ||
/** | ||
* When turned on, respond with the last cache (if available) immediately, even if it has | ||
* expired, and update the cache in the background. | ||
*/ | ||
noWait?: boolean; | ||
}): Fn; | ||
@@ -59,5 +65,7 @@ export default function throttle(handler: (this: any, ...args: any[]) => any, options: number | { | ||
for?: any; | ||
noWait?: boolean; | ||
}) { | ||
const key = typeof options === "number" ? null : options.for; | ||
const duration = typeof options === "number" ? options : options.duration; | ||
const noWait = typeof options === "number" ? false : !!options?.noWait; | ||
@@ -69,3 +77,3 @@ const handleCall = function ( | ||
) { | ||
if (cache.result && Date.now() < (cache.expires ?? 0)) { | ||
if (cache.result && ((cache.pending && noWait) || Date.now() < (cache.expires ?? 0))) { | ||
if (cache.result.error) { | ||
@@ -76,2 +84,4 @@ throw cache.result.error; | ||
} | ||
} else if (cache.pending) { | ||
return cache.pending; | ||
} | ||
@@ -81,5 +91,30 @@ | ||
const returns = handler.call(this, ...args); | ||
cache.result = { value: returns }; | ||
cache.expires = Date.now() + duration; | ||
return returns; | ||
if (typeof returns?.then === "function") { | ||
cache.pending = (returns as Promise<any>).then(value => { | ||
cache.pending = undefined; | ||
cache.result = { value }; | ||
cache.expires = Date.now() + duration; | ||
return value; | ||
}).catch(error => { | ||
cache.pending = undefined; | ||
cache.result = { error }; | ||
cache.expires = Date.now() + duration; | ||
throw error; | ||
}); | ||
if (noWait && cache.result) { | ||
if (cache.result.error) { | ||
throw cache.result.error; | ||
} else { | ||
return cache.result.value; | ||
} | ||
} else { | ||
return cache.pending; | ||
} | ||
} else { | ||
cache.result = { value: returns }; | ||
cache.expires = Date.now() + duration; | ||
return returns; | ||
} | ||
} catch (error) { | ||
@@ -86,0 +121,0 @@ cache.result = { error }; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
609143
7868
990