2016-05-15 23 views
2

是否有任何示例使用cycle-http製作任意數量的順序依賴http請求?使用`cycle-http`製作任意數量的順序依賴請求。

我想從API獲取每個頁面,其中只能使用當前頁面中的數據創建下一個請求。

我試過去適應this answer使用Observable.merge(),但我不知道如何掛接到cycle-http源&接收器。

引用

+0

我覺得困難是如果一個cycle.js驅動程序被用來做出每個後續請求,那麼如何將每個請求中的數據轉發到下一個週期呢? – bloodyKnuckles

+0

'@cycle/fetch'採用通過驅動程序循環執行的任意鍵/值。可以在'requests'屬性返回時被檢索,但是在mergeAll之後會被重新獲取。 – bloodyKnuckles

回答

3

下面是使用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,如果存在,並將其存儲在累加器中。

1

它會更好,如果你提供一些代碼示例。然而基本邏輯可以是這樣的:

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$ 
 
    }; 
 
}

  1. 響應流的初始請求

代碼將是這樣映射到一個請求流

  • 開始請求流

  • 0

    所以這很可能是可怕的過於複雜,我應該把它廢鋼 適當嘗試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.