如何使用JavaScript在链中使用firebase onSnapshot?

如何使用JavaScript在链中使用firebase onSnapshot?,javascript,firebase,google-cloud-firestore,Javascript,Firebase,Google Cloud Firestore,我想按顺序使用firebase的onSnapshot函数。下面给出了我想应用此功能的情况 情景: firestore中有2个系列员工和项目。在Employees集合中,文档存储员工的详细信息。它还存储该特定员工正在处理的项目文档的ID。在Projects集合中,存储项目的详细信息 目标: 首先,我必须从与特定员工相关的Employees集合中获取数据。然后,从获取的员工数据中,我将获得他/她正在处理的项目ID。因此,我需要从该ID获取项目详细信息。因此,当任何与项目或员工相关的信息发生变化时,屏

我想按顺序使用firebase的onSnapshot函数。下面给出了我想应用此功能的情况

情景:

firestore中有2个系列<代码>员工和
项目
。在
Employees
集合中,文档存储员工的详细信息。它还存储该特定员工正在处理的
项目
文档的ID。在
Projects
集合中,存储项目的详细信息

目标:

首先,我必须从与特定员工相关的
Employees
集合中获取数据。然后,从获取的员工数据中,我将获得他/她正在处理的项目ID。因此,我需要从该ID获取项目详细信息。因此,当任何与项目或员工相关的信息发生变化时,屏幕上的数据也应该实时变化

问题:

我试图编写嵌套代码。但它只对员工数据实时工作。更新项目详细信息时,它不会更改。像这样的

admin.auth().onAuthStateChanged(async () => {
    if (check_field(admin.auth().currentUser)) {
        await db.collection('Employees').doc(admin.auth().currentUser.uid).onSnapshot(snap => {
            ...
            let project_details = new Promise(resolve => {
                let projects = [];
                for (let i in snap.data().projects_list) {
                    db.collection('Projects').doc(snap.data().projects_list[i]).onSnapshot(prj_snap => {
                        let obj = prj_snap.data();
                        obj['doc_id'] = prj_snap.id;
                        projects.push(obj);
                    });
                }
                resolve(projects);
            });

            Promise.all([project_details]).then(items => {
                ...
                // UI updation
            });
            ...
        });
    }
});

这样做的正确方法是什么?

您实际上提出了一个非常复杂的数据流场景。我认为这是一个多步骤的问题。你的目标基本上是:

  • 如果有用户,请实时侦听该用户的项目ID列表
  • 对于每个项目id,实时收听该项目的详细信息
  • (大概)清理不再相关的听众
  • 所以我会这样处理:

    let uid;
    let employeeUnsub;
    let projectIds = [];
    let projectUnsubs = {};
    let projectData = {};
    
    const employeesRef = firebase.firestore().collection('Employees');
    const projectsRef = firebase.firestore().collection('Projects');
    
    firebase.auth().onAuthStateChanged(user => {
      // if there is already a listener but the user signs out or changes, unsubscribe
      if (employeeUnsub && (!user || user.uid !== uid)) {
        employeeUnsub();
      }
    
      if (user) {
        uid = user.uid;
        // subscribe to the employee data and trigger a listener update on changes
        employeeUnsub = employeesRef.doc(uid).onSnapshot(snap => {
          projectIds = snap.get('projects_list');
          updateProjectListeners();
        });
      }
    });
    
    function updateProjectListeners() {
      // get a list of existing projects being listened already
      let existingListeners = Object.keys(projectUnsubs);
      for (const pid of existingListeners) {
        // unsubscribe and remove the listener/data if no longer in the list
        if (!projectIds.includes(pid)) {
          projectUnsubs[pid]();
          delete projectUnsubs[pid];
          delete projectData[pid];
          render();
        }
      }
    
      for (const pid of projectIds) {
        // if we're already listening, nothing to do so skip ahead
        if (projectUnsubs[pid]) { continue; }
        // subscribe to project data and trigger a render on change
        projectUnsubs[pid] = projectsRef.doc(pid).onSnapshot(snap => {
          projectData[pid] = snap.data);
          render();
        });
      }
    }
    
    function render() {
      const out = "<ul>\n";
      for (const pid of projectIds) {
        if (!projectData[pid]) {
          out += `<li class="loading">Loading...</li>\n`;
        } else {
          const project = projectData[pid];
          out += `<li>${project.name}</li>`;
        }
      }
      out += "</ul>\n";
    }
    
    let-uid;
    让雇员成为不明嫌犯;
    设射影=[];
    让projectUnsubs={};
    让projectData={};
    const employeesRef=firebase.firestore().collection('Employees');
    const projectsRef=firebase.firestore().collection('Projects');
    firebase.auth().onAuthStateChanged(用户=>{
    //如果已有侦听器,但用户注销或更改,请取消订阅
    if(employeeUnsub&(!user | | user.uid!==uid)){
    雇员不明嫌犯();
    }
    如果(用户){
    uid=user.uid;
    //订阅员工数据并在更改时触发侦听器更新
    employeeUnsub=employeesRef.doc(uid).onSnapshot(snap=>{
    projectds=snap.get('projects_list');
    updateProjectListeners();
    });
    }
    });
    函数updateProjectListeners(){
    //获取已侦听的现有项目的列表
    让existingListeners=Object.keys(projectUnsubs);
    for(现有侦听器的常量pid){
    //如果不再在列表中,请取消订阅并删除侦听器/数据
    如果(!projectId.includes(pid)){
    projectUnsubs[pid]();
    删除projectUnsubs[pid];
    删除项目数据[pid];
    render();
    }
    }
    for(项目的常量pid){
    //如果我们已经在听,没有什么可以做的,请跳到前面
    if(projectUnsubs[pid]){continue;}
    //订阅项目数据并在更改时触发渲染
    projectUnsubs[pid]=projectsRef.doc(pid).onSnapshot(snap=>{
    项目数据[pid]=快照数据);
    render();
    });
    }
    }
    函数render(){
    const out=“
      \n”; for(项目的常量pid){ 如果(!projectData[pid]){ out+=`
    • 加载…
    • \n`; }否则{ const project=projectData[pid]; out+=`
    • ${project.name}
    • `; } } out+=“
    \n”; }
    上面的代码就是您所说的(在本例中,
    render()
    函数只返回一个字符串,但是您可以执行任何您想要实际操作DOM/显示数据的操作)


    这是一个很长的例子,但您所谈论的是一个非常复杂的概念,即在实时数据发生变化时动态地连接实时数据。希望这能给你一些前进的指导

    太好了!非常感谢。你消除了我的疑虑。我尝试过这样做,但效果很好。:)