我的节点项目当前包含一个嵌套的回调圣诞树,以便获取数据并按正确的顺序处理它们。现在我尝试使用Promises进行重构,但我不确定如何正确地进行重构。
让我们说我拿到一份办事处清单,然后为每个办公室提供所有员工,然后是每个员工'薪水。最后,所有实体(办公室,员工和工资)应链接在一起并存储在数据库中。
一些说明我当前代码的伪代码(省略错误处理):
fetch(officesEndpoint, function (data, response) {
parse(data, function (err, offices) {
offices.forEach(function (office) {
save(office);
fetch(employeesEndPoint, function (data, response) {
parse(data, function (err, employees) {
// link each employee to office
save(office);
save(employee);
employees.forEach(function () {
fetch(salaryEndpoint, function (data, response) {
parse(data, function (err, salaries) {
// link salary to employee
save(employee);
});
});
});
});
});
});
});
});
我尝试用promises解决这个问题,但我有几个问题:
saveEmployees
职能部门,我只能访问员工,而不是进一步链接的办公室:var restClient = require('node-rest-client');
var client = new restClient.Client();
var xml2js = require('xml2js');
// some imaginary endpoints
var officesEndpoint = 'http://api/offices';
var employeesEndpoint = 'http://api/offices/employees';
var salaryEndpoint = 'http://api/employees/:id/salary';
function fetch (url) {
return new Promise(function (resolve, reject) {
client.get(url, function (data, response) {
if (response.statusCode !== 200) {
reject(statusCode);
}
resolve(data);
});
});
}
function parse (data) {
return new Promise(function (resolve, reject) {
xml2js.parseString(data, function (err, result) {
if (err) {
reject(err);
}
resolve(result);
});
});
}
function saveOffices (offices) {
var saveOffice = function (office) {
return new Promise(function (resolve, reject) {
setTimeout(function () { // simulating async save()
console.log('saved office in mongodb');
resolve(office);
}, 500);
})
}
return Promise.all(offices.map(saveOffice));
}
function saveEmployees (employees) {
var saveEmployee = function (employee) {
return new Promise(function (resolve, reject) {
setTimeout(function () { // simulating async save()
console.log('saved employee in mongodb');
resolve(office);
}, 500);
})
}
return Promise.all(offices.map(saveEmployee));
}
fetch(officesEndpoint)
.then(parse)
.then(saveOffices)
.then(function (savedOffices) {
console.log('all offices saved!', savedOffices);
return savedOffices;
})
.then(function (savedOffices) {
fetch(employeesEndPoint)
.then(parse)
.then(saveEmployees)
.then(function (savedEmployees) {
// repeat the chain for fetching salaries?
})
})
.catch(function (error) {
console.log('something went wrong:', error);
});
答案 0 :(得分:1)
你没有必要筑巢,这也会有效:
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);
});
答案 1 :(得分:0)
这是改变这个的好方法
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
问题 - 如果您需要访问多个返回值(即办公室和工作岗位),您基本上有两种选择:
嵌套承诺 - 如果它“有意义”,这通常不是很糟糕。如果逻辑需要,Altought承诺可以避免巨大的回调嵌套,可以嵌套承诺。
拥有“全局”变量 - 您可以在承诺范围内定义变量并将结果保存到它,因此承诺将这些变量用作“全局”(在其范围内)。
答案 2 :(得分:0)
您的宣传功能fetch
,parse
,saveOffice
和ssaveEmployee
都可以。有了这些,你可以重构你当前的代码,使用promises,chain而不是nest,并省去一堆错误处理样板:s
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
全部链接起来。你无法真正避免这种嵌套。
您将获得大量保存结果数组数组的承诺,或者整个过程中的任何错误。