You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JavaScript、React和Cypress重构嵌套then代码并修复报错?

问题原因
  • 首先排查变量名笔误:你代码上层定义的子家族变量名为subfamily,但后续查询时使用的是subfamily1.name,如果实际代码里没有定义subfamily1变量,会导致查询参数错误,返回空数组。
  • 核心偶现错误原因:和你之前封装waitForItems的场景一致,API写入和查询存在数据延迟,你调用cy.apiGetSubFamily时没有等待数据同步完成,偶尔后台还没完成之前的写入操作就返回了空的查询结果,此时subfamily[0]为undefined,读取id就会报错。
  • 额外问题:多层嵌套的then形成回调地狱,代码可读性差,中间变量全部挂载在外部作用域,容易出现变量污染和作用域问题。
修复方案

第一步:封装通用API等待重试函数

把你之前写的waitForItems逻辑抽象成通用函数,所有查询操作都可以复用,避免空结果问题:

export const waitForApiResult = (apiCall, predicate, retries = 5, interval = 1000) => {
  return cy.wrap(null).then(() => {
    return apiCall().then(result => {
      if (predicate(result) || retries <= 0) {
        expect(predicate(result)).to.be.true
        return result
      }
      cy.wait(interval)
      return waitForApiResult(apiCall, predicate, retries - 1, interval)
    })
  })
}

第二步:用Cypress别名替代外部变量+扁平化链式调用

用Cypress内置的as()方法存储中间结果,不需要嵌套then,也不需要在外部定义变量存值,完全规避作用域问题。

重构后完整代码
// 通用重试函数复用原有waitForItems逻辑
export const waitForApiResult = (apiCall, predicate, retries = 5, interval = 1000) => {
  return cy.wrap(null).then(() => {
    return apiCall().then(result => {
      if (predicate(result) || retries <= 0) {
        expect(predicate(result)).to.be.true
        return result
      }
      cy.wait(interval)
      return waitForApiResult(apiCall, predicate, retries - 1, interval)
    })
  })
}

export const waitForItems = (retries, nrItems) => {
  return waitForApiResult(
    () => cy.apiGetItems(),
    items => items.length === nrItems,
    retries
  )
}

const item = { name: 'name1' }
const itemFamilyName = 'ItemFamily1'
const subfamily1 = { name: 'subfamily1' } // 修正变量名和后续调用一致

describe('some test', () => {
  before(() => {
    cy.clickButtonByLabel('Click')
    switchUser(USERNAME)
    waitForItems(NUMBEROFRETRIES, NUMBEROFITEMS)

    // 获取item id并存储为别名
    cy.apiGetItems({ name: item.name })
      .its('0.id')
      .as('itemId')

    // 获取item family id
    cy.get('@itemId')
      .then(() => cy.apiGetItemFamily({ name: itemFamilyName }))
      .its('0.id')
      .as('familyId')

    // 加重试逻辑获取subfamily,从根源避免空结果报错
    cy.get('@familyId')
      .then(familyId => {
        return waitForApiResult(
          () => cy.apiGetSubFamily({
            name: subfamily1.name,
            item__family__id__in: familyId
          }),
          subfamily => subfamily.length > 0
        )
      })
      .its('0.id')
      .as('subfamilyId')

    // 提交something
    cy.all(['@itemId', '@subfamilyId'])
      .then(([itemId, subfamilyId]) => {
        return cy.apiPostSomething({
          ...something,
          items: [itemId],
          subfamily: [subfamilyId]
        })
      })
      .its('id')
      .as('somethingId')

    // 提交变量值
    cy.get('@subfamilyId')
      .then(subfamilyId => cy.apiGetItemVariable({
        key: ItemVariableName,
        subfamily__id__in: subfamilyId
      }))
      .its('0.id')
      .then(variableId => {
        return cy.apiPostVariableValue({
          variable: variableId,
          value: controlValue
        })
      })

    // 最后执行patch操作
    cy.all(['@somethingId'])
      .then(([somethingId]) => {
        return cy.apiGetsubFamily({ name: subfamily1.name })
      })
      .then(newControl => {
        const latestVersionControl = newControl[0].versions.find(v => v.version === '3.9.0')
        return cy.apiPatchSomething({
          id: somethingId,
          controls: [latestVersionControl.id]
        })
      })
  })
})

注:如果你的Cypress版本没有内置cy.all方法,可以用依次调用cy.get('@别名')的方式获取多个存储值,不会影响功能运行。

内容的提问来源于stack exchange,提问作者stackuser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 19:15:01