Reputation: 592
In my web application I can run blocks of code (it creates a promise and waits for the result to come out). Every time a user runs a paragraph, I add it's id to an array and run it sequentially.
runSequentially(paragraphsId) {
paragraphsId.reduce((promise, paragraphId) => {
return promise.then(() => this.runParagraph(paragraphId))
}, Promise.resolve())
}
addToQueue(paragraphId) {
if (this.state.runQueue.indexOf(paragraphId) === -1) {
this.setState({
runQueue: [...this.state.runQueue, paragraphId]
}, () => this.runSequentially(this.state.runQueue))
}
}
runParagraph(paragraphId) {
const newParagraphResults = { ...this.state.paragraphResults }
delete newParagraphResults[paragraphId]
const newParagraphs = { ...this.state.paragraphs }
const newParagraph = newParagraphs[paragraphId]
newParagraph.isRunning = true
newParagraph.status = 'running'
this.setState({
paragraphs: newParagraphs,
paragraphResults: newParagraphResults
})
const paragraphs = [
{
identifiers: { id: paragraphId },
title: newParagraph.title,
source: newParagraph.source
}
]
const notebookLibraries = Object.values(this.state.notebookLibraries)
this.runController = new AbortController()
return this.service.notebookRun(this.notebookId, paragraphs, notebookLibraries, this.runController)
.then(result => {
Object.entries(result.paragraphs).forEach(entry => {
if (entry[0] === 'default_paragraph') {
return
}
const paragraphId = entry[0]
const paragraphResult = entry[1]
newParagraphResults[paragraphId] = paragraphResult
paragraphResult.exception ? this.setParagraph(paragraphId, { status: 'failed' }) :
this.setParagraph(paragraphId, { status: 'passed' })
})
this.setState({ paragraphResults: newParagraphResults })
})
.catch((error) => {
if (error.name === 'AbortError') {
return Promise.reject(error)
}
const message = `Execution failed for reason: ${error.reason}.`
this.handleServiceError('notebook', 'run', message)
})
.finally(() => {
const newRunQueue = [ ...this.state.runQueue ]
newRunQueue.shift()
this.setParagraph(paragraphId, { isRunning: false })
this.setState({ runQueue: newRunQueue })
})
}
When a user runs a paragraph we call addToQueue
which then calls runSequentially
. We shift the queue when a promise is settled (in the runParagraph
method) but if we run another paragraph before the first one has finished this will iterate over the same promise twice.
How would you handle this dynamic queue of promises ? Could recursivity work in this case ?
Upvotes: 0
Views: 392
Reputation: 664297
if we run another paragraph before the first one has finished this will iterate over the same promise twice.
You will need to keep the promise
queue as a property in your state, instead of creating a new Promise.resolve()
every time you call runSequentially
. See here for an example implementation.
If you want to manage your queue strictly through setState
, you should not need a runSequentially
method at all. runParagraph
itself would a) check whether it already is running and b) when finished dequeue the next element from the array and call runParagraph
again until there a none left.
Upvotes: 2
Reputation: 51816
You should initialize another property (perhaps queue
is not the best name since you already have state.runQueue
) in your class to Promise.resolve()
, and let that be the pending promise of your sequential queue. Then you can do something like this:
runSequentially(...paragraphsId) {
this.queue = paragraphsId.reduce((promise, paragraphId) => {
return promise.then(() => this.runParagraph(paragraphId))
}, this.queue)
}
addToQueue(paragraphId) {
if (this.state.runQueue.indexOf(paragraphId) === -1) {
this.setState({
runQueue: [...this.state.runQueue, paragraphId]
}, () => this.runSequentially(paragraphId))
}
}
runSequentially()
now accepts incremental updates rather than the entire runQueue
, and you shouldn't store queue
on your state
variable because the promise itself doesn't affect your render, so it's safe.
Upvotes: 2