2個前の記事で作った loadMoreTask に不具合が見つかったので改善してみた。
修正前
# views/loadMore.coffee
angular.module("app").factory "view.loadMoreTask", ->
return (startPage, loader) ->
self =
pageNo: startPage
items: []
isComplete: false
loadMore: ->
loader(self.pageNo++).then (response) ->
Array.prototype.push.apply(self.items, response.items)
self.isComplete = response.isComplete
return self
この実装では、 loader の response が page = 1 の時だけ異様に遅く、先に page = 2 の方が返ってきてしまった場合、items の中身がてれこになってしまう。
改良版はこちら
angular.module("amo.view", []).factory "amo.view.loadMoreTask", ["$q", ($q) ->
return (startPage, loader) ->
_current = _start = $q.when startPage
self =
items: []
isComplete: false
loadMore: ->
_current = _current.then (page) ->
loader(page).then (response) ->
Array.prototype.push.apply self.items, response.items
self.isComplete = response.isComplete
return page + 1
reset: ->
self.items = []
self.isComplete = false
_current = _start
]
-
_current
はpromise
であり、page
を包み込んでいる -
loadMore
が呼ばれると、まず_current.then
を使って 現在のpage
を受け取る -
loader
を使ってそのpage
の内容を読み込む - 結果を元に
self.items
,self.isComplete
を更新し、page + 1
を返す - この時点で、
loader(page).then (response) -> return page + 1
は値page + 1
を包み込んだpromise
である - この
promise
をさらに_current.then (page) -> ...
の部分が返している。promise は 2 重に重ねても 1 度だけ重ねたのと同じであるから、結局この部分はpage + 1
を包み込んだpromise
になる - 最後に、これをまた
_current
に入れているのだから、_current
は promise であり、page + 1
を包み込んでいる -
loadMore
を実行するたび確実にpage
がインクリメントされ、then
を使っているおかげで順番がてれこになることもない。 -
reset
を押せば _current がはじめの状態に戻る。loader は同じpage
に対しては同じ結果を返すようにするべきであり、そのポリシーさえ守れば、このloadMoreTask
は何度でも実行できる。
いやー promise
便利だわ。