将多个函数应用于流中的每个元素

时间:2018-03-24 17:25:04

标签: javascript rxjs reactivex

我正在尝试获取流元素,并一次应用一个元素的一系列函数。每个函数都是几个异步操作之一(下载,图像转换,通过网络上传等)。库使用不同的API(一些Promises,一些回调),因此如果我能弄清楚如何使这个工作,使用Observable可能会简化API。

使用rxjs我正在试图找出正确执行此操作的语法,这是我到目前为止所尝试的:

const Rx = require('rxjs');
const { download } = require('./lib/images');
const { transform } = require('./lib/operations');
const program = require('./lib/bin');
const util = require('./lib/util');

const files = util.fixFileExtensions(program.args);
const files$ = Rx.Observable.fromPromise(getMetadata(files));
const download$ = Rx.Observable.bindCallback(download);
const transform$ = Rx.Observable.fromPromise(transform);

// Take the files, apply the download$ and transform$ to each element in the stream
files$
    .map(download$)
    .map(transform$)
    .subscribe();

但是我的语法已关闭,我收到错误消息。使这项工作的语法是什么?

编辑从当前的评论和答案中,这里是更新的,但它仍未将download的输出传递给transform。这就是我所拥有的:

function download({id, name}, callback) {
    const credentials = {....};
    const filePath = `/path/to/tmp/${name}`;
    const stream = fs.createWriteStream(filePath);
    authenticate(credentials, err {
        if (err) throw err;
        files.download(...., (err, res) => {
            res.data
                .on('end', () => callback(filePath))
                .on('error', err => throw err)
                .on('data', /* write to file */)
                .pipe(stream)
        });
    });
}

function transform(filename, callback) {
     const transformations = /* load transformations */;
     const result = transformations.map(t => applyTransformation(filename, t));
     callback(result); 
}

const download$ = Rx.Observable.bindCallback(download);
const transform$ = Rx.Observable.bindCallback(transform);
files$
    .flatMap(file => file)
    .switchMap(download$)
    .map(transform$)
    .subscribe(console.log, console.error, () => console.log('map is done'));

这会导致我的download函数发出错误TypeError: callback is not a function,因此download被正确调用,但其输出未传递给transform

2 个答案:

答案 0 :(得分:1)

最后,您需要使用switchMap来应用异步操作。 Switchmap将订阅内部Observable并解决它:

files$
    .switchMap(download$)
    .switchMap(transform$)
    .subscribe();

如果订阅中需要下载值,则必须传递内部值:

files$
    .switchMap(download$)
    .switchMap(transform$, (outerValue, innerValue) => {outerValue, innerValue})
    .subscribe(valueOfDonwload => console.log(valueOfDonwload));

答案 1 :(得分:0)

将此留给后人,大卫的回答是在正确的轨道上,但仍然无效。我仔细阅读了这篇博客文章,了解了如何使用Observable序列:https://medium.freecodecamp.org/rxjs-and-node-8f4e0acebc7c

这是我的最终代码:

files$
    .flatMap(file => file)
    .switchMap(input => download$(input))
    .mergeMap(downloadedFile => transform$(downloadedFile))
    .subscribe(console.log, console.error, () => console.log('map is done'));

我没想到的一件事是我的.switchMap(input => download$(input))似乎不等同于.switchMap(download$);后一种语法会抛出错误TypeError: callback is not a function

第二个好奇点是.flatMap.mergeMap是别名 - 它们具有相同的功能,似乎归结为“在序列中获取前一个Observable的输出”。这里有一个细微差别,在我的代码中,第一个flatMap从前一个Observable中分离出数组,第二个(mergeMap)获取前一个输出并将其作为输入提供给transform$ 。由于语法没有任何不同,我不确定它是如何工作的,除非它默认执行此操作

现在开始将上传功能绑定到这个吸盘并继续完全自动化我的侧面演出。