を使用して、任意の数の連続した依存 http リクエストを作成する例はありますcycle-http
か?
現在のページのデータを使用してのみ次のリクエストを行うことができる API からすべてのページを取得したいと考えています。
Observable.merge() を使用するこの回答を適応させようとしましたが、それをcycle-http
ソースとシンクに接続する方法がわかりません。
を使用して、任意の数の連続した依存 http リクエストを作成する例はありますcycle-http
か?
現在のページのデータを使用してのみ次のリクエストを行うことができる API からすべてのページを取得したいと考えています。
Observable.merge() を使用するこの回答を適応させようとしましたが、それをcycle-http
ソースとシンクに接続する方法がわかりません。
これは、 Cycle.jsと@cycle/fetch
ドライバーを使用して、任意の数の連続した依存リクエストを別の方法で処理したものです。
( GitHubユーザー API を使用。users クエリはページごとに 30 ユーザーを返し、since
URL パラメーターはユーザー ID 番号であり、次のユーザー ID でクエリを開始します。)
main
まず、コメント付きの関数の主要部分:
const listResponse$ = sources.FETCH // response returned from FETCH driver
.mergeAll()
.flatMap(res => res.json())
.scan(
((userstotals, users) =>
[
userstotals[0] + 1, // page count
users[29] && users[29].id, // last id on full page
userstotals[2].concat(users) // collect all users
]
),
[0, undefined, []] // default accumulator
)
.share(); // allows stream split
// Branch #1 - cycle again for more pages
const listRequest$ = listResponse$
.filter(users =>
0 < users[1] && // full page id exists
maxpages > users[0]; // less than maxpages
)
.startWith('initial')
.map(users =>
`https:\/\/api.github.com/users?since=${
(!isNaN(parseInt(users[1], 10)) && users[1]) || // last id full page
idstart // default id start
}`
);
// Branch #2 - display
const dom$ = listResponse$
.map(userstotals => div(JSON.stringify(userstotals[2])));
(これは更新された回答ですscan
。sを1つに組み合わせることができることに気付きました。)
sources
説明: 最初にプロパティから応答を取得FETCH
し、フラット化して JSON を取得し、scan
これまでにクエリされたページ数をカウントします。maxpages
照会されたページ数は、所定の数を超えないように後で比較されます。次にid
、存在する場合は完全なページの最後を取得し、最後に、concat
これまでに蓄積されたユーザー ページのコレクションを含む現在のユーザー ページを取得します。応答情報を蓄積した後share
、ストリームを 2 つのブランチに分割できるようにします。
FETCH
最初のブランチは、ドライバーを介してクエリを再循環させ、より多くのページをクエリするために使用されます。ただし、最初filter
に、最後のページとクエリされたページ数を確認します。ID が数値でない場合、最後のページに到達しています。最後のページに達していて、クエリするページがない場合は続行しないでください。また、照会されたページ数が の値を超える場合も続行しないでmaxpages
ください。
2 番目のブランチは、蓄積された応答にアクセスしてユーザーの完全なリストを取得しJSON.stringify
、オブジェクトを仮想 dom オブジェクト (div
メソッド) に変換して、表示のために DOM ドライバーに送信します。
完全なスクリプトは次のとおりです。
import Cycle from '@cycle/rx-run';
import {div, makeDOMDriver} from '@cycle/dom';
import {makeFetchDriver} from '@cycle/fetch';
function main(sources) { // provides properties DOM and FETCH (evt. streams)
const acctok = ''; // put your token here, if necessary
const idstart = 19473200; // where do you want to start?
const maxpages = 10;
const listResponse$ = sources.FETCH
.mergeAll()
.flatMap(res => res.json())
.scan(
((userstotals, users) =>
[
userstotals[0] + 1, // page count
users[29] && users[29].id, // last id on full page
userstotals[2].concat(users) // collect all users
]
),
[0, undefined, []]
)
.share();
const listRequest$ = listResponse$
.filter(function (users) {
return 0 < users[1] && maxpages > users[0];
})
.startWith('initial')
.map(users =>
`https:\/\/api.github.com/users?since=${
(!isNaN(parseInt(users[1], 10)) && users[1]) || // last id full page
idstart // default id start
}` //&access_token=${acctok}`
);
const dom$ = listResponse$
.map(userstotals => div(JSON.stringify(userstotals[2])));
return {
DOM: dom$,
FETCH: listRequest$
};
}
Cycle.run(main, {
DOM: makeDOMDriver('#main-container'),
FETCH: makeFetchDriver()
});
(私の最初の答えは、後世に残しました。2 つscan
の s に注意してください。)
const listResponse$ = sources.FETCH
.mergeAll()
.flatMap(res => res.json())
.scan(((userscount, users) => // <-- scan #1
[userscount[0] + 1, users]), [0, []]
)
.share();
const listRequest$ = listResponse$
.filter(function (users) {
return users[1][29] && users[1][29].id &&
maxpages > users[0];
})
.startWith('initial')
.map(users =>
`https://api.github.com/users?since=${
(users[1][users[1].length-1] && users[1][users[1].length-1].id) ||
idstart
}`
);
const dom$ = listResponse$
.scan(function (usersall, users) { // <-- scan #2
usersall.push(users);
return usersall;
}, [])
.map(res => div(JSON.stringify(res)));
前もって一度 ing することscan
で、存在する場合は完全なページの最後の ID を取得し、それをアキュムレータに格納する必要がありました。
したがって、これは恐ろしく複雑すぎる可能性が高く、Erdalの答えを適切に試すために破棄する必要 がありますが、ここに私が思いついたものがあります...
export default function app({HTTP}) {
const {
allPagesRequest$: staffPagesReq$,
latestData$: staff$,
} = getAllPages({url: '/staff', HTTP});
// staff$ is converted to vdom...
return /* sinks */ {
DOM: staffPagesReq$,
HTTP: staffVdom$,
}
}
const fetchNthPage = (optsIn) => {
const opts = merge(
{
url: '',
page: 0,
HTTP: undefined,
}, optsIn
);
const u = new URI(opts.url)
.setQuery({'_page': opts.page.toString()});
const pageNResponse$ = opts.HTTP
.filter(
res$ => res$.request.url === urlForEndpoint(u)
)
.flatMap(
res$ => res$.catch(
err => Observable.of(
{
body: {'error in response:': err.toString()}
}
)
)
)
.map(res => res.body)
.take(1)
.shareReplay(1);
return Observable.of({
pageNRequest$: Observable.of(basicRequestObject(u)),
pageNResponse$: pageNResponse$,
opts: opts
});
};
const encapsulateAs = typeName => data => {
return {type: typeName, data}
};
const fetchAllPagesIndividually = (optsIn) => {
const opts = merge(
{
url: '',
page: 0,
HTTP: undefined,
},
optsIn
);
return Observable.defer(
() => fetchNthPage(opts)
.flatMap(x => {
const mergedItems$ = Observable
.merge(
x.pageNRequest$.map(encapsulateAs('request')),
x.pageNResponse$.map(encapsulateAs('response'))
);
const optsForNextPage = merge(opts, {
page: opts.page + 1
});
const next$ = Observable
.never() // `next$` shouldn't end when `pageNResponse$` does
.merge(x.pageNResponse$)
.shareReplay(1)
.takeWhile(res => {
//noinspection UnnecessaryLocalVariableJS
let isFullPage = path(['response', 'length'], res) === apiPageSize;
return isFullPage;
})
.flatMap(() => {
return fetchAllPagesIndividually(optsForNextPage)
});
//noinspection UnnecessaryLocalVariableJS
const concattedItem$ = Observable
.concat(
mergedItems$,
next$
);
return concattedItem$
})
.shareReplay(1)
);
};
const concatPages = (acc, currentVal, index, source) => acc.concat(currentVal);
const typeIs = typeStr => compose(
equals(typeStr),
prop('type')
);
const typeNotIn = typesArray => compose(
not,
unary(flip(contains)(typesArray)),
prop('type')
);
const getAllPages = (optsIn) => {
const f$ = fetchAllPagesIndividually(optsIn)
.shareReplay(1);
const allPagesRequest$ = f$
.filter(typeIs('request'))
.map(prop('data'));
const allPagesResponse$ = f$
.filter(typeIs('response'))
.map(prop('data'));
const theRest$ = f$
.filter(typeNotIn(['request', 'response', 'data']));
const latestData$ = allPagesResponse$
.map(prop('response'))
.scan(concatPages);
return {
allPagesRequest$,
allPagesResponse$,
latestData$,
theRest$,
}
};
compose()
、not()
、merge()
、unary()
などはRamda からのものです。