简体   繁体   English

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

[英]Apply several functions to each element in a stream

I'm trying to take a stream elements, and apply a series of functions one element at a time. 我正在尝试采用流元素,并一次将一个元素应用一系列功能。 Each function one of several asynchronous operations (download, image transformations, uploads over a network, etc.). 每个功能都是几种异步操作之一(下载,图像转换,通过网络上传等)。 The libraries use different APIs (some Promises, some callbacks) so using Observables might simplify the API if I can figure out how to make this work. 这些库使用不同的API(一些Promises,一些回调),因此,如果我能弄清楚如何实现此目的,则使用Observables可以简化API。

Using rxjs I'm trying to work out the syntax to do this correctly, this is what I have attempted so far: 我正在尝试使用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();

However my syntax is off and I get an error. 但是我的语法已关闭,并且出现错误。 What's the syntax to make this work? 使这项工作有效的语法是什么?

EDIT From the current comments and answers, here's the updated but it's still not passing the output of download to transform . 编辑从当前的评论和答案来看,这是更新的内容,但仍未将download的输出传递给transform Here's what I have: 这是我所拥有的:

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'));

This throws an error from my download function saying TypeError: callback is not a function , so download is called properly, but its output is not being passed to transform 这从我的download函数抛出一个错误,说TypeError: callback is not a function ,因此正确调用了download ,但是未将其输出传递给transform

Finally you need to take switchMap to apply asynchronous operations. 最后,您需要使用switchMap来应用异步操作。 Switchmap will subscribe to the inner Observable and resolve it: Switchmap将订阅内部的Observable并对其进行解析:

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

If the value of download is needed in the subscription you must pass the inner Value: 如果订阅中需要下载值,则必须传递内部值:

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

Leaving this here for posterity, David's reply is on the right track, but still doesn't work. 将其留在后头供您参考,David的回复是正确的,但仍然行不通。 I managed to make it work after perusing this blog post which shows how to work with Observable sequences: https://medium.freecodecamp.org/rxjs-and-node-8f4e0acebc7c 我仔细阅读了此博客文章,使之能够工作,该文章显示了如何使用可观察序列: https : //medium.freecodecamp.org/rxjs-and-node-8f4e0acebc7c

Here's what my final code looks like: 这是我最终的代码:

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

One thing I didn't expect is that my .switchMap(input => download$(input)) doesn't seem to be equivalent to .switchMap(download$) ; 我没想到的一件事是我的.switchMap(input => download$(input))似乎不等同于.switchMap(download$) ; the latter syntax throws the error TypeError: callback is not a function 后一种语法引发错误TypeError: callback is not a function

A second curious point is that .flatMap and .mergeMap are aliases - they have the same functionality which seems to boil down to "take the output of the previous Observable in the sequence". 第二个好奇点是.flatMap.mergeMap是别名-它们具有相同的功能,这似乎归结为“获取序列中先前Observable的输出”。 There's a nuance here that in my code, the first flatMap splits out the array from the previous Observable and the second one ( mergeMap ) takes the previous output and provides it as input to transform$ . 这里有一个细微差别,在我的代码中,第一个flatMap从先前的Observable中拆分出数组,第二个flatMapmergeMap )获取先前的输出,并将其提供为transform$输入。 Since the syntax isn't any different I'm not sure how that works unless it does this stuff by default 由于语法没有什么不同,除非在默认情况下执行此操作,否则我不确定其工作原理

Now on to tying upload functionality to this sucker and continue to fully automate my side gig. 现在,将上传功能绑定到此傻瓜,并继续完全自动化我的附带演出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM