是否有任何示例使用cycle-http
製作任意數量的順序依賴http請求?使用`cycle-http`製作任意數量的順序依賴請求。
我想從API獲取每個頁面,其中只能使用當前頁面中的數據創建下一個請求。
我試過去適應this answer使用Observable.merge(),但我不知道如何掛接到cycle-http
源&接收器。
是否有任何示例使用cycle-http
製作任意數量的順序依賴http請求?使用`cycle-http`製作任意數量的順序依賴請求。
我想從API獲取每個頁面,其中只能使用當前頁面中的數據創建下一個請求。
我試過去適應this answer使用Observable.merge(),但我不知道如何掛接到cycle-http
源&接收器。
下面是使用Cycle.js和@cycle/fetch
驅動程序進行的任意數量的順序相關請求的另一個處理。
(使用GitHub用戶API。在使用者查詢返回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時,可以合併成一個。)
解釋:一是其拉離sources
財產FETCH
響應,壓平並拉動JSON出來,然後scan
算多少頁到目前爲止查詢。查詢的頁數稍後與maxpages
比較,以便不超過預定的數量。接下來獲取完整頁面的最後一個id
(如果存在),並且最後一個,concat
當前用戶頁面包含迄今積累的用戶頁面集合。在累積響應信息share
之後,可以將它分成兩個分支。
第一個分支用於通過FETCH
驅動程序將查詢重新循環以查詢更多頁面。但首先filter
檢查最後一頁和查詢的頁數。如果該ID不是數字,則已經達到最後一頁。如果已經到達最後一頁,則不要繼續,因此不要再查詢頁面。如果查詢的頁面數量超過maxpages
的值,也不要繼續。
第二個分支只是進入累積響應以獲得用戶的完整列表,然後將該對象轉換爲虛擬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()
});
(。我的第一個答案,爲後人留下了注意兩個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)));
通過scan
荷蘭國際集團一次,然後,我需要抓取整個頁面的最後一個id,如果存在,並將其存儲在累加器中。
它會更好,如果你提供一些代碼示例。然而基本邏輯可以是這樣的:
function main (sources){
const initialRequest = {
url: 'http://www.google.com'
};
const request$ = sources.HTTP
.filter(response$ => /*FILTER LOGIC GOES HERE */)
.switch()//or you can use flatMap
.map(response =>/* MAP RESPONSE TO A NEW REQUEST */)
.startWith(initialRequest);
return {
HTTP: request$
};
}
代碼將是這樣映射到一個請求流
所以這很可能是可怕的過於複雜,我應該把它廢鋼 適當嘗試Erdal's answer, 但這裏是我想出來的......
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.
我覺得困難是如果一個cycle.js驅動程序被用來做出每個後續請求,那麼如何將每個請求中的數據轉發到下一個週期呢? – bloodyKnuckles
'@cycle/fetch'採用通過驅動程序循環執行的任意鍵/值。可以在'requests'屬性返回時被檢索,但是在mergeAll之後會被重新獲取。 – bloodyKnuckles