Вот еще один пример произвольного количества последовательных зависимых запросов с использованием Cycle.js и драйвера @cycle/fetch
.
(Используя GitHub API пользователей. Запрос пользователей возвращает 30 пользователей на страницу, а параметр since
URL представляет собой идентификатор пользователя. число и начинает запрос с следующего идентификатора пользователя.)
Сначала основная часть функции 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
можно объединить в один.)
ОБЪЯСНЕНИЕ. Сначала извлеките ответ из свойства sources
FETCH
, сгладьте его и извлеките JSON, затем scan
, чтобы подсчитать, сколько страниц было запрошено на данный момент. Количество запрошенных страниц позже сравнивается с maxpages
, чтобы не превысить заданное число. Затем получите последнюю id
полной страницы, если она существует, и последнюю, concat
текущую страницу пользователей с коллекцией пользовательских страниц, накопленных на данный момент. После накопления информации об ответе share
поток можно разделить на две ветви.
Первая ветвь используется для повторного прохождения запроса через драйвер FETCH
для запроса дополнительных страниц. Но сначала filter
проверить последнюю страницу и количество запрошенных страниц. Если идентификатор не является числом, то последняя страница была достигнута. Не продолжайте, если последняя страница уже достигнута и, следовательно, нет больше страниц для запроса. Также не продолжайте, если количество запрошенных страниц превышает значение maxpages
.
Вторая ветвь просто обращается к накопленному ответу, чтобы получить полный список пользователей, затем JSON.stringify
s объект и преобразует его в виртуальный объект 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
.)
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
ing один раз, мне нужно было получить полный последний идентификатор страницы, если он существует, и сохранить его в аккумуляторе.
person
bloodyKnuckles
schedule
17.05.2016
@cycle/fetch
принимает произвольные ключи/значения, которые передаются через цикл драйвера. Можно получить при возврате свойстваrequests
, но оно забивается послеmergeAll
. - person bloodyKnuckles   schedule 16.05.2016