Javascript 如何正确地将承诺与嵌套链接起来

Javascript 如何正确地将承诺与嵌套链接起来,javascript,node.js,promise,Javascript,Node.js,Promise,我的节点项目当前包含一个嵌套回调的横向圣诞树,以便获取数据并以正确的顺序处理它们。现在我正在尝试使用承诺重构,但我不确定如何正确地进行重构 比如说,我将获取一份办公室列表,然后是每个办公室的所有员工,然后是每个员工的工资。最终,所有实体(办公室、员工和工资)都应链接在一起并存储在数据库中 一些说明我当前代码的伪代码(省略错误处理): 我试着用承诺来解决这个问题,但我有几个问题: 有点冗长 每个办公室都需要链接到各自的员工,但在saveEmployees功能中,我只能访问员工,而不能从链的更高层

我的节点项目当前包含一个嵌套回调的横向圣诞树,以便获取数据并以正确的顺序处理它们。现在我正在尝试使用承诺重构,但我不确定如何正确地进行重构

比如说,我将获取一份办公室列表,然后是每个办公室的所有员工,然后是每个员工的工资。最终,所有实体(办公室、员工和工资)都应链接在一起并存储在数据库中

一些说明我当前代码的伪代码(省略错误处理):

我试着用承诺来解决这个问题,但我有几个问题:

  • 有点冗长
  • 每个办公室都需要链接到各自的员工,但在
    saveEmployees
    功能中,我只能访问员工,而不能从链的更高层访问办公室:


这是改变这种状况的好办法

        if (response.statusCode !== 200) {
            reject(statusCode);
        }
        resolve(data);
对此

        if (response.statusCode !== 200) {
            return reject(statusCode);
        }
        resolve(data);
在您的示例中,结果是相同的,但是如果您正在做更多的事情(比如在数据库中做一些事情),则可能会出现意外的结果,因为如果不返回,整个方法将被执行

这个例子

var prom = new Promise((resolve,reject) => {
    reject(new Error('error'));
    console.log('What? It did not end');
    resolve('Ok, promise will not be called twice');
});

prom.then(val => {
    console.log(val);
}).catch(err => {
    console.log(err.message);
});
有这个输出吗

What? It did not end
error
对于这个问题-如果您需要访问多个返回值(即办公室和雇员),基本上有两种选择:

  • 嵌套承诺——如果“有意义”,这通常不是坏事。虽然应该承诺可以避免大量回调嵌套,但是如果逻辑需要,嵌套承诺也可以

  • 拥有“全局”变量-您可以在承诺本身的范围内定义变量并将结果保存到它,因此承诺将这些变量用作“全局”(在其范围内)


    • 您不必嵌套,这也可以:

      fetch(officesEndpoint)
        .then(parse)
        .then(saveOffices)
        .then(function(savedOffices) {
          console.log('all offices saved!', savedOffices);
          return savedOffices;
        })
        .then(function(savedOffices) {
          // return a promise
          return fetch(employeesEndPoint); // the returned promise can be more complex, like a Promise.all of fetchEmployeesOfThisOffice(officeId)
        })
        // so you can chain at this level
        .then(parse)
        .then(saveEmployees)
        .then(function(savedEmployees) {
          return fetch(salariesEndPoint);
        })
        .catch(function(error) {
          console.log('something went wrong:', error);
        });
      

      您的预期功能
      获取
      解析
      保存办公室
      保存员工
      都很好。有了这些,您可以重构当前代码以使用承诺、链而不是嵌套(如果适用),并省去一堆错误处理样板文件:

      fetch(officesEndpoint)
      .then(parse)
      .then(function(offices) {
          return Promise.all(offices.map(function(office) {
              return save(office)
              .then(function(){ return fetch(employeesEndPoint); })
              .then(parse)
              .then(function(employees) {
                  // link each employee to office
                  // throw in a Promise.all([save(office), save(employee)]) if needed here
                  return Promise.all(employees.map(function(employee) {
                      return fetch(salaryEndpoint)
                      .then(parse)
                      .then(function(salaries) {
                          return Promise.all(salaries.map(function(salary) {
                              // link salary to employee
                              return save(employee);
                          }));
                      });
                  }));
              });
          }));
      });
      
      在最内部的循环回调中,您可以使用所有的
      office
      employee
      salary
      将它们链接到您喜欢的位置。你无法真正避免这种嵌套


      您将得到一个承诺,即存储结果的大量数组或整个过程中的任何错误。

      听起来您想要嵌套三个循环。不,没有嵌套就没有理由这样做。你使用的是什么承诺库?这个问题不属于这个网站吗?我建议你阅读这篇关于承诺的文章,它将帮助你找到一个解决方案,如果你有机会再次重构,我建议你使用异步。检查这个,它比较了关于JavaScript回调地狱的几种方法。不,不要使用全局变量。请要做到这一点,必须使用承诺。
      fetch(officesEndpoint)
        .then(parse)
        .then(saveOffices)
        .then(function(savedOffices) {
          console.log('all offices saved!', savedOffices);
          return savedOffices;
        })
        .then(function(savedOffices) {
          // return a promise
          return fetch(employeesEndPoint); // the returned promise can be more complex, like a Promise.all of fetchEmployeesOfThisOffice(officeId)
        })
        // so you can chain at this level
        .then(parse)
        .then(saveEmployees)
        .then(function(savedEmployees) {
          return fetch(salariesEndPoint);
        })
        .catch(function(error) {
          console.log('something went wrong:', error);
        });
      
      fetch(officesEndpoint)
      .then(parse)
      .then(function(offices) {
          return Promise.all(offices.map(function(office) {
              return save(office)
              .then(function(){ return fetch(employeesEndPoint); })
              .then(parse)
              .then(function(employees) {
                  // link each employee to office
                  // throw in a Promise.all([save(office), save(employee)]) if needed here
                  return Promise.all(employees.map(function(employee) {
                      return fetch(salaryEndpoint)
                      .then(parse)
                      .then(function(salaries) {
                          return Promise.all(salaries.map(function(salary) {
                              // link salary to employee
                              return save(employee);
                          }));
                      });
                  }));
              });
          }));
      });