仿生人AI服务端测试网页

This commit is contained in:
BBIT-Kai
2025-11-05 18:05:09 +08:00
parent 179604931d
commit f50521e548
14 changed files with 6521 additions and 0 deletions
@@ -0,0 +1,98 @@
export default class BlockingQueue {
#items = [];
#waiters = []; // {resolve, reject, min, timer, onTimeout}
/* 空队列一次性闸门 */
#emptyPromise = null;
#emptyResolve = null;
/* 生产者:把数据塞进去 */
enqueue(item, ...restItems) {
if (restItems.length === 0) {
this.#items.push(item);
}
// 如果有额外参数,批量处理所有项
else {
const items = [item, ...restItems].filter(i => i);
if (items.length === 0) return;
this.#items.push(...items);
}
// 若有空队列闸门,一次性放行所有等待者
if (this.#emptyResolve) {
this.#emptyResolve();
this.#emptyResolve = null;
this.#emptyPromise = null;
}
// 唤醒所有正在等的 waiter
this.#wakeWaiters();
}
/* 消费者:min 条或 timeout ms 先到谁 */
async dequeue(min = 1, timeout = Infinity, onTimeout = null) {
// 1. 若空,等第一次数据到达(所有调用共享同一个 promise)
if (this.#items.length === 0) {
await this.#waitForFirstItem();
}
// 立即满足
if (this.#items.length >= min) {
return this.#flush();
}
// 需要等待
return new Promise((resolve, reject) => {
let timer = null;
const waiter = { resolve, reject, min, onTimeout, timer };
// 超时逻辑
if (Number.isFinite(timeout)) {
waiter.timer = setTimeout(() => {
this.#removeWaiter(waiter);
if (onTimeout) onTimeout(this.#items.length);
resolve(this.#flush());
}, timeout);
}
this.#waiters.push(waiter);
});
}
/* 空队列闸门生成器 */
#waitForFirstItem() {
if (!this.#emptyPromise) {
this.#emptyPromise = new Promise(r => (this.#emptyResolve = r));
}
return this.#emptyPromise;
}
/* 内部:每次数据变动后,检查哪些 waiter 已满足 */
#wakeWaiters() {
for (let i = this.#waiters.length - 1; i >= 0; i--) {
const w = this.#waiters[i];
if (this.#items.length >= w.min) {
this.#removeWaiter(w);
w.resolve(this.#flush());
}
}
}
#removeWaiter(waiter) {
const idx = this.#waiters.indexOf(waiter);
if (idx !== -1) {
this.#waiters.splice(idx, 1);
if (waiter.timer) clearTimeout(waiter.timer);
}
}
#flush() {
const snapshot = [...this.#items];
this.#items.length = 0;
return snapshot;
}
/* 当前缓存长度(不含等待者) */
get length() {
return this.#items.length;
}
}
+37
View File
@@ -0,0 +1,37 @@
import { getLogContainer } from '../document.js'
const logContainer = getLogContainer();
// 日志记录函数
export function log(message, type = 'info') {
// 将消息按换行符分割成多行
const lines = message.split('\n');
const now = new Date();
// const timestamp = `[${now.toLocaleTimeString()}] `;
const timestamp = `[${now.toLocaleTimeString()}.${now.getMilliseconds().toString().padStart(3, '0')}] `;
// 为每一行创建日志条目
lines.forEach((line, index) => {
const logEntry = document.createElement('div');
logEntry.className = `log-entry log-${type}`;
// 如果是第一条日志,显示时间戳
const prefix = index === 0 ? timestamp : ' '.repeat(timestamp.length);
logEntry.textContent = `${prefix}${line}`;
// logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
// logEntry.style 保留起始的空格
logEntry.style.whiteSpace = 'pre';
if (type === 'error') {
logEntry.style.color = 'red';
} else if (type === 'debug') {
logEntry.style.color = 'gray';
return;
} else if (type === 'warning') {
logEntry.style.color = 'orange';
} else if (type === 'success') {
logEntry.style.color = 'green';
} else {
logEntry.style.color = 'black';
}
logContainer.appendChild(logEntry);
});
logContainer.scrollTop = logContainer.scrollHeight;
}