@kayahr 的解决方案非常聪明和优雅。但我不会使用,因为在 Promise 的执行器中中断线程是非常粗鲁的。
谁知道呢,也许有一些 IO 操作。读/写操作。而这样的取消可能是危险的。我的意思是文件也不能关闭或类似的东西。
在我的工作中,我还需要一个“可取消”的承诺。我做了一点其他的解决方案。
这个想法是,它并没有真正取消,这是一种获得更多结果选择的方法。实际上,promise 给了我们 2 + 1 种方式:
-
然后:全部完成,一切正常
-
捕捉:哦,错误
-
终于:做吧,做吧
但是如果出于某种原因我们想停止运营,我们没有这样的选择。
有了这个解决方案,这是可能的。
export type TResolver<T> = (value?: T) => void;
export type TRejector = (error: Error) => void;
export type TFinally = () => void;
export type TCanceler<T> = (reason?: T) => void;
export class CancelablePromise<T, C> {
private _resolvers: Array<TResolver<T>> = [];
private _rejectors: TRejector[] = [];
private _cancelers: Array<TCanceler<C>> = [];
private _finishes: TFinally[] = [];
private _canceled: boolean = false;
private _resolved: boolean = false;
private _rejected: boolean = false;
private _finished: boolean = false;
constructor(
executor: (resolve: TResolver<T>, reject: TRejector, cancel: TCanceler<C>, self: CancelablePromise<T, C>) => void,
) {
const self = this;
// Create and execute native promise
new Promise<T>((resolve: TResolver<T>, reject: TRejector) => {
executor(resolve, reject, this._doCancel.bind(this), self);
}).then((value: T) => {
this._doResolve(value);
}).catch((error: Error) => {
this._doReject(error);
});
}
public then(callback: TResolver<T>): CancelablePromise<T, C> {
this._resolvers.push(callback);
return this;
}
public catch(callback: TRejector): CancelablePromise<T, C> {
this._rejectors.push(callback);
return this;
}
public finally(callback: TFinally): CancelablePromise<T, C> {
this._finishes.push(callback);
return this;
}
public cancel(callback: TCanceler<C>): CancelablePromise<T, C> {
this._cancelers.push(callback);
return this;
}
public break(reason: C): CancelablePromise<T, C> {
this._doCancel(reason);
return this;
}
private _doResolve(value: T) {
if (this._canceled) {
return;
}
this._resolved = true;
this._resolvers.forEach((resolver: TResolver<T>) => {
resolver(value);
});
this._doFinally();
}
private _doReject(error: Error) {
if (this._canceled) {
return;
}
this._rejected = true;
this._rejectors.forEach((rejector: TRejector) => {
rejector(error);
});
this._doFinally();
}
private _doFinally() {
if (this._finished) {
return;
}
this._finished = true;
this._finishes.forEach((handler: TFinally) => {
handler();
});
}
private _doCancel(reason?: C) {
if (this._resolved || this._rejected || this._canceled) {
// Doesn't make sence to cancel, because it was resolved or rejected or canceled already
return this;
}
this._canceled = true;
this._cancelers.forEach((cancler: TCanceler<C>) => {
cancler(reason);
});
this._doFinally();
}
}
让我们看看它是如何工作的。
const a: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel) => {
// Do some long operation
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log('resolved');
}).finally(() => {
console.log('finally done');
});
很像正常的承诺。在输出中:
resolved
finally done
让我们尝试拒绝:
const b: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel) => {
// Do some long operation
setTimeout(() => {
console.log('timer #1');
resolve();
}, 1000);
setTimeout(() => {
console.log('timer #2');
reject(new Error('Because I can!'));
}, 500);
}).then(() => {
console.log('resolved');
}).catch((error: Error) => {
console.log('error');
}).finally(() => {
console.log('finally done');
});
输出:
timer #2
error
finally done
timer #1
现在我们将取消它
const c: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel) => {
// Do some long operation
setTimeout(() => {
console.log('timer #1');
resolve();
}, 1000);
setTimeout(() => {
console.log('timer #2');
reject(new Error('Because I can!'));
}, 500);
setTimeout(() => {
console.log('timer #3');
cancel();
}, 250);
}).then(() => {
console.log('resolved');
}).cancel(() => {
console.log('canceled');
}).catch((error: Error) => {
console.log('error');
});
输出
timer #3
timer #2
canceled
timer #1
很好,我们没有触发 resolve 或 reject,但是(!)我们的执行器在 promise 被取消后仍然工作。这不好,这里有一种方法可以修复它并以真正的方式取消它。
const d: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel, self) => {
// Listen cancel inside executer to cancel all correctly
self.cancel(() => {
clearTimeout(t1);
clearTimeout(t2);
});
// Do some long operation
const t1: any = setTimeout(() => {
console.log('timer #1');
resolve();
}, 1000);
const t2: any = setTimeout(() => {
console.log('timer #2');
reject(new Error('Because I can!'));
}, 500);
setTimeout(() => {
console.log('timer #3');
cancel();
}, 250);
}).then(() => {
console.log('resolved');
}).cancel(() => {
console.log('canceled');
}).catch((error: Error) => {
console.log('error');
});
输出
timer #3
canceled
因此,计时器 #1 和 #2 已停止 - 承诺被小心取消。
当然我们也可以在外面取消。
const d: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel, self) => {
// Listen cancel inside executer to cancel all correctly
self.cancel(() => {
clearTimeout(t1);
clearTimeout(t2);
});
// Do some long operation
const t1: any = setTimeout(() => {
console.log('timer #1');
resolve();
}, 1000);
const t2: any = setTimeout(() => {
console.log('timer #2');
reject(new Error('Because I can!'));
}, 500);
}).then(() => {
console.log('resolved');
}).cancel(() => {
console.log('canceled');
}).catch((error: Error) => {
console.log('error');
});
setTimeout(() => {
console.log('timer #3 (outside)');
d.break();
}, 250);
结果会一样:
timer #3 (outside)
canceled
希望它有用。