如何使用typescript给promise增加一个属性?

例如下图的代码,promise找不到cancel属性 [图片] function sleep(timeout: number): Promise<an…
关注者
4
被浏览
6,614

3 个回答

诺,这是你想要的:

interface Promise<T> {
    cancelWith (v: T): void
}

function sleep(timeout: number): Promise<'done' | undefined> {
    let res!: (v: 'done' | undefined) => void
    const promise = new Promise<'done' | undefined>(resolve => {
        res = resolve
    
        setTimeout(() => {
            resolve('done')
        }, timeout)
    })
    promise.cancelWith = res;
    return promise
}

但是俺想不明白为啥要给 promise 增加属性,这岂不是会让其他人误用 Promise,虽然没有污染原型,但污染了类型呀?建议这样写:

interface CancelablePromise<T> extends Promise<T> {
    cancelWith (v: T): void
}

function cancelableSleep(timeout: number): CancelablePromise<'done' | undefined> {
    let res!: (v: 'done' | undefined) => void
    const promise = new Promise<'done' | undefined>(resolve => {
        res = resolve
    
        setTimeout(() => {
            resolve('done')
        }, timeout)
    })
    
    return Object.assign(promise, { cancelWith: res })
}

其实俺也不建议这样写,俺就是顺着你思路来了这么一下子,俺自己大概会这样写吧:

function sleep(timeout: number) {
    return new Promise(resolve => setTimeout(resolve, timeout))
}

function createControlable<T>() {
    let resolve!: (v: Promise<T> | T) => void
    let reject!: () => void

    const promise = new Promise<T>((res, rej) => {
        resolve = res
        reject = rej
    });

    return Object.assign(promise, {
        resolve,
        reject,
    })
}

interface ControlSignals<T> {
    abort (): void
    hang(): void
    cancelWith(v: T): void
}

function withControlSignals<T>(p: Promise<T>): Promise<T> & ControlSignals<T> {
    const controlable = createControlable<T>();

    const racing = Promise.race([
        p.then(v => (controlable.resolve(v), controlable)),
        controlable
    ])

    return Object.assign(racing, {
        abort() {
            controlable.reject()
        },
        hang() {
            controlable.resolve(new Promise<T>(() => {}))
        },
        cancelWith(v: T) {
            controlable.resolve(v)
        },
    })
}

const test1 = withControlSignals(sleep(5000).then(() => 'test1'))
// 5 秒后正常输出 test1
test1.then(console.log)

const test2 = withControlSignals(sleep(5000).then(() => 'test2'))
test2.then(console.log)
// 5 秒后 throw 一个 error,可以 async try await catch finally
test2.abort()

const test3 = withControlSignals(sleep(5000).then(() => 'test3'))
test3.then(console.log)
// 啥也不搞
test3.hang()

const test4 = withControlSignals(sleep(5000).then(() => 'test4'))
test4.then(console.log)
// 用自定义的输出
test4.cancelWith('BAZINGA')

写的有点复杂了(过度设计了嘛?),不过俺寻思你的需求是可以解构出一个原始的 sleep,一个控制流的,估摸这样子用接口表达出来以后更方便扩展吧?可组合可复用,专注点也分离掉了...

type CancelablePromise = Promise<any> & {cancel: any}

function sleep(timeout: number): CancelablePromise  {
let res
let promise = new Promise(resolve => {
res = resolve
setTimeout(() => {
resolve('done')
}, timeout)
}) as CancelablePromise
promise.cancel = res;
return promise
}