Chain将observable加入到一个订阅中

时间:2016-07-28 17:25:16

标签: typescript angular rxjs

我有一个名为'Item'的模型类,它需要从后端加载两个单独的图像(精确的精灵纹理和阴影纹理)。我实现了纹理到我的Item中的并行加载和最终连接,这样我就可以在其他地方订阅它。

现在,当我使用Base64将文件传输到我的Angular2应用程序时,我已经有了这个工作。但现在我想要处理普通的blob。虽然Angular2尚不支持,但完全可以读取响应的private _body属性。问题是我的模型类应该将图像检索为HTMLImageElements,并将数据解析为Base64数据网址。要从我的blob生成此数据URL,我需要使用基于回调的FileReader.readAsDataUrl()。我想我已经想出如何将这个回调包装成一个Observable(如果这种方法错了,请纠正我,因为我现在无法确认)。

所以,现在我根本无法弄清楚如何正确地链接我的调用,以便能够订阅生成的Observable然后生成我的Item,就像这样ItemService.getItem(1).subscribe(item => ...)

当前的设置给了我一个奇怪的错误,说明订阅方法没有在生成的Observable上定义。我对RxJS很新,如果有人能告诉我如何正确设置它,我会很高兴:)

/* implementation in ItemService */

getItem(id:number):Observable<Item> {
    return Observable.forkJoin( // join both texture fetches
        this.http.get('/api/items/' + id + '/sprite'),  // fetch sprite texture
        this.http.get('/api/items/' + id + '/shadow'),  // fetch shadow texture
        (spriteResponse, shadowResponse) => {
            // transform responses into proper blobs
            const spriteBlob = new Blob([spriteResponse._body], {type: 'image/png'})
            const shadowBlob = new Blob([shadowResponse._body], {type: 'image/png'})
            return [spriteBlob, shadowBlob]
        })
        .flatMap(result => Observable.forkJoin( // chain with joined image generation (pretty sure this is wrong somehow)
            ItemService.generateImage(result[0]), // parse spriteBlob to image
            ItemService.generateImage(result[1]), // parse shadowBlob to image
            (spriteImage, shadowImage) => {
                // assemble model from images
                const item = new Item()
                item.setSprite(spriteImage)
                item.setShadow(shadowImage)
                return item
            })
        )
}

static generateImage(data:Blob):Observable<HTMLImageElement> {
    const img = new Image() // create new HTML Image
    const reader = new FileReader() // init file reader
    reader.readAsDataURL(data)  // transform blob to base64 data url

    // create observable from callback (is this correct?)
    return Observable.bindCallback(reader.onloadend, () => {
        img.src = reader.result
        return img
    })
}

1 个答案:

答案 0 :(得分:2)

问题是双重的。首先,bindCallback返回函数,在调用时返回Observable,它不会返回Observable。绑定回调的想法是,您正在将通常通过回调将其异步结果报告的函数转换为Observable

在这种情况下,您实际上正在等待要触发的事件(loadend),因此您可能希望改为使用fromEvent

static generateImage(data:Blob):Observable<HTMLImageElement> {
    const reader = new FileReader() // init file reader
    reader.readAsDataURL(data)  // transform blob to base64 data url

    // create observable from callback (is this correct?)
    return Observable.fromEvent(reader, 'load', (e) => {
       var img = new Image();
       img.src = reader.result;
       return img;
    }).first();
}

现在这不是非常强大,但应该完成工作。如果你想看一个更完整的例子,有一个为RxJS4编写的fromReader方法,你可以根据自己的需要编写它(RxJ5的版本还没有实现)。