Tengo una serie de métodos (algunos asincrónicos y otros no) que me gustaría usar bluebird.each para procesar en orden. Aquí hay un ejemplo simplificado:
import bluebird from 'bluebird'; const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); class Template { propertyA!: number; propertyB!: number; constructor() {} async methodA() { this.propertyA = 10; console.log({ thisArgInA: this, propertyA: this.propertyA }); await delay(500); return this.propertyA; } methodB() { this.propertyB = this.propertyA * 100; console.log({ thisArgInB: this, propertyB: this.propertyB }); return this.propertyB; } } const instance = new Template(); const sequence = [instance.methodA, instance.methodB]; (async function main() { await bluebird.each(sequence, (fn) => { const result = fn.call(instance); console.log({ result }); }); })();Esto produce este error que no entiendo:
index.ts:27:20 - error TS2684: The 'this' context of type '(() => Promise<number>) | (() => number)' is not assignable to method's 'this' of type '(this: Template) => Promise<number>'. Type '() => number' is not assignable to type '(this: Template) => Promise<number>'. Type 'number' is not assignable to type 'Promise<number>'. 27 const result = fn.call(instance); Pensé que tal vez se deba a que bluebird.each acepta valores o promesas que se resuelven en valores que el compilador no puede conciliar, pero la única solución que se me ocurrió fue escribir fuertemente los valores devueltos"
async methodA: Promise<number> { /*...*/ } methodB: number { /*...*/ }Pero eso no cambió nada. También tomo nota de lo siguiente:
each devolución de llamada se pierde la conexión con la Template como thismethodA es síncrono (elimine la palabra clave async , elimine el delay() , simplemente devuelva this.propertyA , esto funciona bien.return , las cosas funcionan bien.fn.call(instance) a fn.bind(instance)() funciona bien ¿Hay una manera más fácil de llamar a estos métodos en secuencia para que se mantenga su conexión con this ?
Puede ser útil observar los tipos inferidos:
sequence : (((this: Template) => number) | ((this: Template) => Promise<number>))[] o simplemente ((() => Promise<number>) | (() => number))[]fn : ((this: Template) => number) | ((this: Template) => Promise<number>) o simplemente (() => Promise<number>) | (() => number).call : <Template, [], number>(this: (this: Template) => number, thisArg: Template): number y podemos ver que el tipo de la expresión .call() no es lo que esperábamos: el compilador quiere una función que devuelva un number y se queja porque fn alternativamente puede devolver una Promise<number> . Esto probablemente se deba a un error de TypeScript al tratar de inferir el tipo de retorno de los tipos de unión de funciones. Podría estar relacionado con la sutil diferencia entre X => A | B y (X => A) | (X => B) .
Podemos arreglar esto escribiendo
const result = fn.call<Template,[],number|Promise<number>>(instance); o…(fn: () => number | Promise<number>)) => … oconst sequence : (() => number | Promise<number>)[]