Skip to content

Non-debounce option #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,32 @@ new Vue({
}
```

## Cancel debouncing

Normally, computed properties work with debouncing
in the way the result of a change is not committed if other changes are triggered before the result is committed.
For example, if a source property is continuously changing every 100ms and API call takes 500ms,
normal async computed properties will not be changed until the change of the source property stops.
With non-debounce option, async computed properties will be updated with the last source value.

```js
new Vue({
data: {
searchText: ""
},
asyncComputed: {
calculatedValue: {
debounce: false,
get () {
return fetch.get('/some-api/' + searchText)
.then(response => response.data.value)
}
}
}
}
```


## Error handling

By default, in case of a rejected promise in an async computed property, vue-async-computed will take care of logging the error for you.
Expand Down
29 changes: 27 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,15 @@ const AsyncComputed = {
}

for (const key in this.$options.asyncComputed || {}) {
const item = this.$options.asyncComputed[key]
const debounce = (pluginOptions.debounce == null && item.debounce == null) ? null : (pluginOptions.debounce !== false && item.debounce !== false)
let promiseId = 0
let reflectedPromiseId = 0
this.$watch(prefix + key, newPromise => {
if (debounce === true && promiseId !== reflectedPromiseId) {
return
}

const thisPromise = ++promiseId

if (newPromise === DidNotUpdate) {
Expand All @@ -71,10 +78,28 @@ const AsyncComputed = {
}

newPromise.then(value => {
if (thisPromise !== promiseId) return
if (debounce == null) {
if (thisPromise !== promiseId) {
return
}
} else {
if (thisPromise <= reflectedPromiseId) {
return
}
reflectedPromiseId = thisPromise
}
this[key] = value
}).catch(err => {
if (thisPromise !== promiseId) return
if (debounce == null) {
if (thisPromise !== promiseId) {
return
}
} else {
if (thisPromise <= reflectedPromiseId) {
return
}
reflectedPromiseId = thisPromise
}

if (pluginOptions.errorHandler === false) return

Expand Down
67 changes: 67 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -716,3 +716,70 @@ test("shouldUpdate works with lazy", t => {
})
})
})

test("Async computed values are consistent in repeatable change in non-debounce mode", t => {
t.plan(2)
const wait = (time) => new Promise(resolve => {
setTimeout(() => {
resolve()
}, time)
})
const updateInterval = 40
const apiExecutionTimes = [20, 70, 120, 70, 20]
const completedTimes = apiExecutionTimes.map((executionTime, index) => {
return index * updateInterval + executionTime
})
let lastCompletedIndex = -1
const expectedValue = completedTimes.map((completedTime, index) => index)
.sort((a, b) => completedTimes[a] - completedTimes[b])
.filter(index => {
if (index > lastCompletedIndex) {
lastCompletedIndex = index
return true
}
})
console.log('completedTimes', completedTimes)
console.log('expectedValue', expectedValue)
const apiResultValue = []
let apiCalllCount = 0

const mockApi = async (value) => {
const executionTime = apiExecutionTimes[apiCalllCount]
++apiCalllCount
await wait(executionTime)
return value
}

const vm = new Vue({
created () {
console.log('length', apiExecutionTimes.length);
(async () => {
for (let i = 1; i < apiExecutionTimes.length; ++i) {
await wait(updateInterval)
this.srcValue = this.srcValue + 1
}
await wait(apiExecutionTimes.slice(-1)[0] + 10)
t.deepEqual(apiResultValue, expectedValue)
t.equal(vm.dstValue, expectedValue.slice(-1)[0])
})()
},
data () {
return {
srcValue: 0,
}
},
watch: {
dstValue (dstValue) {
apiResultValue.push(dstValue)
},
},
asyncComputed: {
dstValue: {
debounce: false,
get () {
return mockApi(this.srcValue)
},
},
},
})
})