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 便利だわ。