通过捕获承诺提供占位符数据

2025-06-04

通过捕获承诺提供占位符数据

最近,我编写了一个简单的 Slack 机器人,它会通过 API 查询我们产品的反馈,然后将反馈发布到指定的频道。该机器人还允许用户投票决定某条反馈是否可行(例如:“本课程中有一个拼写错误” vs. “可以”或“无可奉告”)。

Slack 消息截图 -

由于这是一个“黑客日”项目,最初的实现名副其实,非常 hacky——投票不会被服务器存储;用户可以随意投票。投票是通过修改 POST/handle-vote请求中传入的字符串来处理的(Slack 作为持久层😂)。

// text => 'Yes: 0 No: 0'
// value => 'yes' || 'no

function updateAttachmentText(text, value) {
  votes = text.split(' ')
  if (value === 'no') {
    votes[3] = parseInt(votes[3]) + 1
  } else if (value === 'yes') {
    votes[1] = parseInt(votes[1]) + 1
  }
  return votes.join(' ')
}

const updated = updateAttachmentText('Yes: 0 No: 0', 'yes')
// => 'Yes: 1 No: 0'
Enter fullscreen mode Exit fullscreen mode

这个小机器人对我们的产品团队来说非常有用 - 但我知道它的黑暗、可怕的秘密,并决定编写一个更强大的版本,使用 Redis 来存储投票数据 - 它将存储投票者的 Slack 用户 ID 并防止一个用户多次投票。

机器人本身使用 cron 任务向频道发布新的反馈。升级时,我在该脚本中添加了一个步骤,以该反馈的 ID 创建一条新的“空白”记录。

const initialVotes = { votes: { yes: [], no: [] } }
redisClient.store(id, JSON.stringify(initialVotes))
Enter fullscreen mode Exit fullscreen mode

一旦用户点击按钮,服务器就会收到请求,通过其 ID 查找反馈,将用户的 ID 添加到正确的列表(“是”或“否”),然后在执行一些逻辑以确保用户只能投票一次且只能以一种方式投票后将其保存回 Redis 存储。

这里的问题在于来自原始机器人的消息 - 这些反馈在我们的应用程序中没有与其 ID 关联的记录;因此,如果用户点击投票按钮,以下代码将会失败:

// Imagine our Redis client setup...
class Redis {
  // setup, etc

  fetch(key) {
    return new Promise((resolve, reject) => {
      this.client.get(key, (err, response) => {
        if (err || !response) { 
          return reject('Unable to find result for', key, err) 
        }
        return resolve(response)
      })
    })
  }
}

// ... and a Vote loading class...
class Vote {
  constructor(id, redisClient) { 
    this.id = id 
    this.redisClient = redisClient 
  }

  loadVote() {
    return this.redisClient.fetch(this.id)
      .then(voteData => JSON.parse(voteData))
  }
}

const vote = new Vote(someId, someRedisClient)

vote.loadVote().then((vote) => incrementCounterAndSave(vote)) 
// Uncaught rejection :(
Enter fullscreen mode Exit fullscreen mode

起初我以为这会是一个很烦人的问题,因为我需要条件逻辑来处理服务器代码中不存在的记录。Vote然而,查看类的代码本身,我发现了一个更简洁的选择:

class Vote {
  // ...
  loadVote() {
    return this.redisClient.fetch(this.id)
      .then(voteData => JSON.parse(voteData))
      .catch((err) => {
        console.log('Encountered an error, returning placeholder data:', err)
        return { votes: { yes: [], no: [] } }
      })
  }
}

const vote = new Vote(someId, someRedisClient)

vote.loadVote()
  .then((vote) => {
    console.log(vote)
    incrementCounterAndSave(vote)
  }) 
// Encountered an error, returning placeholder data: 
//   'Unable to find result for someId (error here)
// { votes: { yes: [], no: [] } }
Enter fullscreen mode Exit fullscreen mode

我认为我应该把这个写下来,因为虽然我已经使用 Promises 有一段时间了,但这个概念并不是我的第一直觉:我没有想到catch在任何地方使用它,只是在我的调用链的最后then

这里有一些代码,您可以在控制台中试用它们,它们可以非常简单地演示这一点!

class DataFetcher {
  constructor() {
    this.count = 0
  }

  fetch() {
    this.count += 1
    return new Promise((resolve, reject) => {
      // Cause this Promise to be rejected/fail every other time the fetch function is called.
      this.count % 2 === 0 ? resolve('data from DB!') : reject('data not found')
    })
  }
}

const client = new DataFetcher()

const getData = () => {
  return client.fetch()
    .then((res) => {
      return res
    })
    .catch(err => {
      return 'placeholder data'
    })
}

getData.then(console.log) // placeholder data
getData.then(console.log) //data from DB!
Enter fullscreen mode Exit fullscreen mode

附注:您可以使用async/以嵌套较少(并且可以说更易读)的方式完全编写此代码await- 无论哪种方式,我都感觉不太强烈,所以我只是使用了 Promises。

编码愉快!

文章来源:https://dev.to/annarankin/supplying-placeholder-data-by-捕捉-promises-372l
PREV
👾 GitHub Spray:绘制你的 GitHub 贡献图
NEXT
让你的职场更适合女性的十个技巧