我怎样才能顺序而动态地执行承诺

时间:2018-12-28 14:12:39

标签: javascript reactjs promise

在我的Web应用程序中,我可以运行代码块(它创建一个Promise,然后等待结果出来)。每次用户运行一个段落时,我都会将其ID添加到数组中并按顺序运行。

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 })
        })
}

当用户运行段落时,我们调用addToQueue,然后调用runSequentially。我们在达成承诺时(在runParagraph方法中转移了队列,但是如果我们在第一个段落完成之前运行另一个段落,它将对相同的承诺进行两次迭代。

您将如何处理动态的Promise队列?递归在这种情况下可以工作吗?

2 个答案:

答案 0 :(得分:2)

您应该在班级中初始化另一个属性(也许queue不是最好的名称,因为您已经拥有state.runQueue),并将其设为Promise.resolve(),并将其作为序列的未完成承诺队列。然后您可以执行以下操作:

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()现在接受增量更新,而不是整个runQueue,并且您不应该将queue存储在您的state变量中,因为承诺本身不会影响您渲染,所以很安全。

答案 1 :(得分:2)

  

如果我们在第一个段落完成之前运行另一个段落,这将对相同的诺言进行两次迭代。

您需要将promise队列作为属性保留在您的状态,而不是每次调用Promise.resolve()时都创建一个新的runSequentially。有关示例实现,请参见here

如果要严格通过setState管理队列,则完全不需要runSequentially方法。 runParagraph本身会(a)检查它是否已经在运行,并且(b)完成后从数组中取出下一个元素,然后再次调用runParagraph直到没有剩余。