mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
feat: CancelableTask&Fallback
This commit is contained in:
107
src/common/cancel-task.ts
Normal file
107
src/common/cancel-task.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
type TaskExecutor<T> = (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void, onCancel: (callback: () => void) => void) => void;
|
||||
|
||||
export class CancelableTask<T> {
|
||||
private promise: Promise<T>;
|
||||
private cancelCallback: (() => void) | null = null;
|
||||
private isCanceled = false;
|
||||
private cancelListeners: Array<() => void> = [];
|
||||
|
||||
constructor(executor: TaskExecutor<T>) {
|
||||
this.promise = new Promise<T>((resolve, reject) => {
|
||||
const onCancel = (callback: () => void) => {
|
||||
this.cancelCallback = callback;
|
||||
};
|
||||
|
||||
executor(
|
||||
(value) => {
|
||||
if (!this.isCanceled) {
|
||||
resolve(value);
|
||||
}
|
||||
},
|
||||
(reason) => {
|
||||
if (!this.isCanceled) {
|
||||
reject(reason);
|
||||
}
|
||||
},
|
||||
onCancel
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
if (this.cancelCallback) {
|
||||
this.cancelCallback();
|
||||
}
|
||||
this.isCanceled = true;
|
||||
this.cancelListeners.forEach(listener => listener());
|
||||
}
|
||||
|
||||
public isTaskCanceled(): boolean {
|
||||
return this.isCanceled;
|
||||
}
|
||||
|
||||
public onCancel(listener: () => void) {
|
||||
this.cancelListeners.push(listener);
|
||||
}
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
||||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.promise.then(onfulfilled, onrejected);
|
||||
}
|
||||
|
||||
public catch<TResult = never>(
|
||||
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
|
||||
): Promise<T | TResult> {
|
||||
return this.promise.catch(onrejected);
|
||||
}
|
||||
|
||||
public finally(onfinally?: (() => void) | undefined | null): Promise<T> {
|
||||
return this.promise.finally(onfinally);
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator]() {
|
||||
return {
|
||||
next: () => this.promise.then(value => ({ value, done: true })),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function demoAwait() {
|
||||
const executor: TaskExecutor<number> = (resolve, reject, onCancel) => {
|
||||
let count = 0;
|
||||
const intervalId = setInterval(() => {
|
||||
count++;
|
||||
console.log(`Task is running... Count: ${count}`);
|
||||
if (count === 5) {
|
||||
clearInterval(intervalId);
|
||||
resolve(count);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
onCancel(() => {
|
||||
clearInterval(intervalId);
|
||||
console.log('Task has been canceled.');
|
||||
reject(new Error('Task was canceled'));
|
||||
});
|
||||
};
|
||||
|
||||
const task = new CancelableTask(executor);
|
||||
|
||||
task.onCancel(() => {
|
||||
console.log('Cancel listener triggered.');
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
task.cancel(); // 取消任务
|
||||
}, 6000);
|
||||
|
||||
try {
|
||||
const result = await task;
|
||||
console.log(`Task completed with result: ${result}`);
|
||||
} catch (error) {
|
||||
console.error('Task failed:', error);
|
||||
}
|
||||
}
|
26
src/common/fall-back.ts
Normal file
26
src/common/fall-back.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
type Handler<T> = () => T | Promise<T>;
|
||||
|
||||
export class Fallback<T> {
|
||||
private handlers: Handler<T>[] = [];
|
||||
|
||||
add(handler: Handler<T>): this {
|
||||
this.handlers.push(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
// 执行处理程序链
|
||||
async run(): Promise<T> {
|
||||
const errors: Error[] = [];
|
||||
for (const handler of this.handlers) {
|
||||
try {
|
||||
const result = await handler();
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
}
|
||||
throw new AggregateError(errors, 'All handlers failed');
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ import { InstanceContext, NapCatCore, ProfileBizType } from '..';
|
||||
import { solveAsyncProblem } from '@/common/helper';
|
||||
import { promisify } from 'node:util';
|
||||
import { LRUCache } from '@/common/lru-cache';
|
||||
import { Fallback } from '@/common/fall-back';
|
||||
|
||||
export class NTQQUserApi {
|
||||
context: InstanceContext;
|
||||
@@ -169,46 +170,44 @@ export class NTQQUserApi {
|
||||
return skey;
|
||||
}
|
||||
|
||||
async getUidByUinV2(Uin: string) {
|
||||
if (!Uin) {
|
||||
async getUidByUinV2(uin: string) {
|
||||
if (!uin) {
|
||||
return '';
|
||||
}
|
||||
const services = [
|
||||
() => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined),
|
||||
() => promisify<string, string[], Map<string, string>>
|
||||
(this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined),
|
||||
() => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined),
|
||||
() => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined),
|
||||
];
|
||||
let uid: string | undefined = undefined;
|
||||
for (const service of services) {
|
||||
uid = await service();
|
||||
if (uid && uid.indexOf('*') == -1 && uid !== '') {
|
||||
break;
|
||||
}
|
||||
|
||||
let isValidUin = (uin: string | undefined) => {
|
||||
if (uin !== undefined && uin !== '0' && uin !== '') { return uin; } throw new Error('uin is invalid');
|
||||
}
|
||||
|
||||
const fallback = new Fallback<string>()
|
||||
.add(async () => isValidUin(await this.context.session.getUixConvertService().getUid([uin]).then((data) => data.uidInfo.get(uin))))
|
||||
.add(() =>isValidUin(this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [uin]).get(uin)))
|
||||
.add(async () => isValidUin(await this.context.session.getGroupService().getUidByUins([uin]).then((data) => data.uids.get(uin))))
|
||||
.add(async () => isValidUin(await this.getUserDetailInfoByUin(uin).then((data) => data.detail.uid)));
|
||||
|
||||
const uid = await fallback.run().catch(() => '0');
|
||||
return uid ?? '';
|
||||
}
|
||||
|
||||
async getUinByUidV2(Uid: string) {
|
||||
if (!Uid) {
|
||||
async getUinByUidV2(uid: string) {
|
||||
if (!uid) {
|
||||
return '0';
|
||||
}
|
||||
const services = [
|
||||
() => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined),
|
||||
() => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined),
|
||||
() => promisify<string, string[], Map<string, string>>
|
||||
(this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined),
|
||||
() => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined),
|
||||
() => this.getUserDetailInfo(Uid).then((data) => data.uin).catch(() => undefined),
|
||||
];
|
||||
let uin: string | undefined = undefined;
|
||||
for (const service of services) {
|
||||
uin = await service();
|
||||
if (uin && uin !== '0' && uin !== '') {
|
||||
break;
|
||||
|
||||
let isValidUid = (uid: string | undefined) => {
|
||||
if (uid !== undefined && uid.indexOf('*') === -1 && uid !== '') {
|
||||
return uid;
|
||||
}
|
||||
throw new Error('uid is invalid');
|
||||
}
|
||||
|
||||
const fallback = new Fallback<string>()
|
||||
.add(async () => isValidUid(await this.context.session.getUixConvertService().getUin([uid]).then((data) => data.uinInfo.get(uid))))
|
||||
.add(() =>isValidUid(this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [uid]).get(uid)))
|
||||
.add(async () => isValidUid(await this.context.session.getGroupService().getUinByUids([uid]).then((data) => data.uins.get(uid))))
|
||||
.add(async () => isValidUid(await this.getUserDetailInfo(uid).then((data) => data.uin)));
|
||||
|
||||
const uin = await fallback.run().catch(() => '0');
|
||||
return uin ?? '0';
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user