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

如何等待eventListener触发后再执行Promise后续链式调用

问题描述

开发中需要等待动态渲染选择列表的change事件监听器捕获到用户选择操作后,再执行fetch Promise链的后续异步逻辑,最终把用户选中的值传入Promise回调,供后续接口请求使用。
现有实现逻辑为:通过fetch拉取接口数据后渲染下拉选项列表,但要求在用户触发选择前,不执行Promise链中后续的.then(resp => resp.json())逻辑。

问题复现代码

页面HTML代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Dynamic List Test</title>
</head>
<body>
  <div class="container">
    <!-- App entry point -->
  </div>
  <script type="text/javascript" src="app.js"></script>
</body>
</html>

业务JS代码

const testUrl =
  'https://hub.dummyapis.com/employee?noofRecords=10&idStarts=1001';

const result = fetch(testUrl, {
  method: 'GET',
})
  .then(resp => resp.json())
  .then(data => {
    // 创建选择下拉框
    const mainEl = document.querySelector('.container');
    let selectList = document.createElement('select');
    selectList.id = 'selectorList';
    mainEl.appendChild(selectList);
    // 组装选项数据
    let nameArray = [];
    const nameList = data;
    nameArray.push(nameList);
    console.log(nameArray[0]);
    
    for (let last in nameArray[0]) {
      let surname = nameArray[0][last].lastName;
      let option = document.createElement('option');
      option.value = surname;
      option.text = surname;
      selectList.appendChild(option);
    }

    let selectorChoice = '';
    // 绑定下拉框变更事件
    selectList.addEventListener('change', e => {
      selectorChoice = e.target.value;
      console.log(selectorChoice);
      return selectorChoice;
    });
  })
  .then(resp => resp.json()) // 期望等待事件监听器触发后再执行这一步
  .catch(error => console.log('Error:', error));

result.then(resp => {
  console.log(resp);
});

报错现象

代码运行后控制台抛出错误:TypeError: Cannot read properties of undefined (reading 'json'),无法正确获取用户选中的选项值,控制台完整输出如下:

(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]0: {id: 1001, imageUrl: 'https://hub.dummyapis.com/Image?text=CA&height=120&width=120', firstName: 'Colt', lastName: 'Altenwerth', email: 'Colt.Altenwerth@dummyapis.com', …}1: {id: 1002, imageUrl: 'https://hub.dummyapis.com/Image?text=HW&height=120&width=120', firstName: 'Helena', lastName: 'Ward', email: 'Helena.Ward@dummyapis.com', …}2: {id: 1003, imageUrl: 'https://hub.dummyapis.com/Image?text=LC&height=120&width=120', firstName: 'Liliana', lastName: 'Connelly', email: 'Liliana.Connelly@dummyapis.com', …}3: {id: 1004, imageUrl: 'https://hub.dummyapis.com/Image?text=MH&height=120&width=120', firstName: 'Myah', lastName: 'Hane', email: 'Myah.Hane@dummyapis.com', …}4: {id: 1005, imageUrl: 'https://hub.dummyapis.com/Image?text=MS&height=120&width=120', firstName: 'Mauricio', lastName: 'Stracke', email: 'Mauricio.Stracke@dummyapis.com', …}5: {id: 1006, imageUrl: 'https://hub.dummyapis.com/Image?text=CS&height=120&width=120', firstName: 'Candice', lastName: 'Sipes', email: 'Candice.Sipes@dummyapis.com', …}6: {id: 1007, imageUrl: 'https://hub.dummyapis.com/Image?text=EA&height=120&width=120', firstName: 'Evangeline', lastName: 'Aufderhar', email: 'Evangeline.Aufderhar@dummyapis.com', …}7: {id: 1008, imageUrl: 'https://hub.dummyapis.com/Image?text=RR&height=120&width=120', firstName: 'Rosetta', lastName: 'Rodriguez', email: 'Rosetta.Rodriguez@dummyapis.com', …}8: {id: 1009, imageUrl: 'https://hub.dummyapis.com/Image?text=TD&height=120&width=120', firstName: 'Tina', lastName: "D'Amore", email: "Tina.D'Amore@dummyapis.com", …}9: {id: 1010, imageUrl: 'https://hub.dummyapis.com/Image?text=PS&height=120&width=120', firstName: 'Pearline', lastName: 'Sawayn', email: 'Pearline.Sawayn@dummyapis.com', …}length: 10[[Prototype]]: Array(0)
app.js:37 Error: TypeError: Cannot read properties of undefined (reading 'json')
    at app.js:36:22
app.js:40 undefined
app.js:32 Hane
问题原因
  • 事件监听器的return只会在事件触发时执行,不会把返回值传给外层Promise链,渲染完下拉框的.then回调没有任何返回值,所以下一层.then接收到的参数是undefined,调用.json()自然报错。
  • DOM事件是异步触发的,原生Promise链不会自动等待用户交互事件触发,需要手动封装Promise把事件回调和Promise链关联起来。
  • 最后多余的.then(resp => resp.json())完全没有必要:第一次fetch拿到的响应已经在最开始转成json了,用户选值本身是字符串,不需要再调用json解析方法。
修复方案

在渲染完下拉框后,返回一个新的Promise,在change事件触发时调用resolve把用户选中的值传出去,删掉多余的json解析步骤即可。

修复后完整JS代码

const testUrl =
  'https://hub.dummyapis.com/employee?noofRecords=10&idStarts=1001';

const result = fetch(testUrl, {
  method: 'GET',
})
  .then(resp => resp.json())
  .then(data => {
    // 创建选择下拉框
    const mainEl = document.querySelector('.container');
    let selectList = document.createElement('select');
    selectList.id = 'selectorList';
    mainEl.appendChild(selectList);
    // 组装选项数据
    for (let item of data) {
      let surname = item.lastName;
      let option = document.createElement('option');
      option.value = surname;
      option.text = surname;
      selectList.appendChild(option);
    }

    // 返回新Promise等待用户选择
    return new Promise(resolve => {
      selectList.addEventListener('change', e => {
        const selectorChoice = e.target.value;
        console.log(selectorChoice);
        resolve(selectorChoice);
      }, { once: true }); // 加once参数避免事件重复绑定
    })
  })
  .catch(error => console.log('Error:', error));

// 这里拿到的就是用户选中的值,可直接用于后续fetch请求
result.then(userSelected => {
  console.log('用户选中的值:', userSelected);
  // 后续业务逻辑,比如携带userSelected发起新的fetch请求
});
关键改动点
  • 去掉了多余的nameArray嵌套,直接遍历接口返回的data数组生成选项,逻辑更简洁。
  • 渲染完下拉框后返回封装的Promise,把change事件的回调和Promise的resolve绑定,只有用户触发选择时Promise才会落定,后续的链式逻辑才会执行。
  • 事件绑定加了{ once: true }配置,用户选择后事件监听器会自动移除,避免内存泄漏。
  • 删除了多余的.then(resp => resp.json())逻辑,用户选择返回的是字符串值,不需要做json解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:15:53