使用promises

时间:2016-05-16 11:19:21

标签: javascript recursion typescript pagination promise

我想用promises调用一个分页API。一开始就不知道有多少页可用,但每次响应都会提供。详细说明我正在调用Jira search,分页信息部分如下:

{
    "startAt": 0,
    "maxResults": 15,
    "total": 100000,
    ...
}

我解决了递归的分页处理问题,这是我在Typescript中的解决方案:

search(jql: string, fields: string[] = [], maxResults = 15) : Promise<JiraIssue[]> {
    // container for the issues
    let issues: Array<JiraIssue> = new Array<JiraIssue>();

    // define recursive function to collect the issues per page and
    // perform a recursive call to fetch the next page
    let recursiveFn = (jql: string, fields: string[], startAt: number, maxResults: number) :
        Promise<JiraIssue[]> => {
        return this
            // retrieves one page
            .searchPage(jql, fields, startAt, maxResults)
            .then((searchResult: JiraSearchResult) => {
                // saves the result of the retrieved page
                issues.push.apply(issues, searchResult.issues);
                if (searchResult.startAt + searchResult.maxResults < searchResult.total) {
                    // retrieves the next page with a recursive call
                    return recursiveFn(jql, fields,
                        searchResult.startAt + searchResult.maxResults,
                        maxResults);
                }
                else {
                    // returns the collected issues
                    return issues;
                }
            })

    };

    // and execute it
    return recursiveFn(jql, fields, 0, maxResults);
}

但是,我不喜欢递归方法,因为这适用于小结果集(我担心堆栈溢出)。你怎么用非递归方法解决这个问题呢?

2 个答案:

答案 0 :(得分:2)

这不是实际的递归,并且没有堆栈溢出危险,因为函数是在then处理程序内调用的。

答案 1 :(得分:1)

一种选择是将它包装在迭代器模式中。

类似的东西:

interface Searcher {
    (jql: string, fields: string[], startAt: number, maxResults: number) => Promise<JiraSearchResult>;
}

class ResultsIterator {
    private jql: string;
    private fields: string[];
    private searcher: Searcher;
    private startAt: number;
    private maxResults: number;
    private currentPromise: Promise<JiraIssue[]>;
    private total: number;

    constructor(searcher: Searcher, jql: string, fields?: string[], maxResults?: number) {
        this.jql = jql;
        this.startAt = 0;
        this.searcher = searcher;
        this.fields = fields || [];
        this.maxResults = maxResults || 15;
        this.total = -1;
    }

    hasNext(): boolean {
        return this.total < 0 || this.startAt < this.total;
    }

    next(): Promise<JiraIssue[]> {
        if (!this.hasNext()) {
            throw new Error("iterator depleted");
        }

        return this.searcher(this.jql, this.fields, this.startAt, this.maxResults)
                    .then((searchResult: JiraSearchResult) => {
                        this.total = searchResult.total;
                        this.startAt = searchResult.startAt + searchResult.maxResults;

                        return searchResult.issues;
                    });
    }
}

这段代码并不完美,因为我不完全确定你在那里做什么(比如什么是this.searchPage?),但你应该得到我的目标。

你会这样做:

if (resultIterator.hasNext()) {
    resultIterator.next().then(...);
}

希望这有帮助。