使用节点异步函数避免 Promise 反模式
Avoiding Promise anti-patterns with node asynchronous functions
我已经意识到 Promise 反模式,担心我可能会在这里上当(请参阅函数内的代码摘录)。可以看出,我将 Promises 嵌套在两个节点异步函数中。我能够公开内部 Promise 的唯一方法是使用外部 Promise。我欢迎有关如何更优雅地编写此内容的指导。
function xyz() {
return new Promise(function(resolve, reject) {
return Resto.find({recommendation: {$gte: 0}}, function (err, data) {
if (err) return reject("makeSitemap: Error reading database");
return fs.readFile(__dirname + '/../../views/sitemap.jade', function(err, file) {
if (err) return reject("makeSitemap: Error reading sitemap template");
[snip]
resolve(Promise.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]));
});
});
});
}
我也发现了这个问题 plnkr
最好的解决方案是创建回调函数的承诺版本。 bluebird,一个优秀的 promise 实现(比 Node 的原生实现更好),内置了这个。
Bluebird.promisifyAll(Resto); // If you can promisify the prototype, that’s better
Bluebird.promisifyAll(fs);
function xyz() {
return Resto.findAsync({recommendation: {$gte: 0}}).then(function (data) {
return fs.readFileAsync(__dirname + '/../../views/sitemap.jade').then(function (file) {
…
return Bluebird.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]);
});
});
}
此外,如果 fs.readFile
不依赖于 Resto.findAsync
,您可能应该 运行 它们在一起:
return Bluebird.all([
Resto.findAsync({recommendation: {$gte: 0}}),
fs.readFileAsync(__dirname + '/../../views/sitemap.jade'),
]).spread(function (data, file) {
…
return Bluebird.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2),
]);
});
根据 minitech 的建议,我写道:
readFileAsync = function(fname) {
return new Promise(function(resolve, reject) {
fs.readFile(fname, function (err, data) {
if (err) return reject(err);
resolve(data);
})
});
};
然后根据 robertklep 的观察,Mongoose 可以 return 一个承诺,我最终得到:
var datasets; // seemingly unavoidable semi-global variable with simple Promises
return Resto.find({recommendation: {$gte: 0}})
.then(function(data) {
datasets = _.partition(data, function(r) {
return _.includes([0,1], r.recommendation);
});
return readFileAsync(__dirname + '/../../views/sitemap.jade');
})
.then(function(file) {
[snip]
return Promise.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]);
});
我可能会继续使用
处理全局变量
因为这被标记了 es6-promise
我想留下一个不使用扩展的 es6 答案:
var xyz = () => new Promise((r, e) => Resto.find({recommendation: {$gte: 0}},
err => err ? e(err) : r()))
.then(() => new Promise((r, e) =>
fs.readFile(__dirname + '/../../views/sitemap.jade',
err => err? e(err) : r())))
// [snip]
.then(() => Promise.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]));
要避免的反模式:
- 嵌套。尽可能压平链条。狭义地使用
new Promise
。
- 隐藏错误。尽可能说出失败的真正原因。
- 扔绳子。抛出真正的错误对象,因为它们有堆栈跟踪。
我已经意识到 Promise 反模式,担心我可能会在这里上当(请参阅函数内的代码摘录)。可以看出,我将 Promises 嵌套在两个节点异步函数中。我能够公开内部 Promise 的唯一方法是使用外部 Promise。我欢迎有关如何更优雅地编写此内容的指导。
function xyz() {
return new Promise(function(resolve, reject) {
return Resto.find({recommendation: {$gte: 0}}, function (err, data) {
if (err) return reject("makeSitemap: Error reading database");
return fs.readFile(__dirname + '/../../views/sitemap.jade', function(err, file) {
if (err) return reject("makeSitemap: Error reading sitemap template");
[snip]
resolve(Promise.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]));
});
});
});
}
我也发现了这个问题 plnkr
最好的解决方案是创建回调函数的承诺版本。 bluebird,一个优秀的 promise 实现(比 Node 的原生实现更好),内置了这个。
Bluebird.promisifyAll(Resto); // If you can promisify the prototype, that’s better
Bluebird.promisifyAll(fs);
function xyz() {
return Resto.findAsync({recommendation: {$gte: 0}}).then(function (data) {
return fs.readFileAsync(__dirname + '/../../views/sitemap.jade').then(function (file) {
…
return Bluebird.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]);
});
});
}
此外,如果 fs.readFile
不依赖于 Resto.findAsync
,您可能应该 运行 它们在一起:
return Bluebird.all([
Resto.findAsync({recommendation: {$gte: 0}}),
fs.readFileAsync(__dirname + '/../../views/sitemap.jade'),
]).spread(function (data, file) {
…
return Bluebird.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2),
]);
});
根据 minitech 的建议,我写道:
readFileAsync = function(fname) {
return new Promise(function(resolve, reject) {
fs.readFile(fname, function (err, data) {
if (err) return reject(err);
resolve(data);
})
});
};
然后根据 robertklep 的观察,Mongoose 可以 return 一个承诺,我最终得到:
var datasets; // seemingly unavoidable semi-global variable with simple Promises
return Resto.find({recommendation: {$gte: 0}})
.then(function(data) {
datasets = _.partition(data, function(r) {
return _.includes([0,1], r.recommendation);
});
return readFileAsync(__dirname + '/../../views/sitemap.jade');
})
.then(function(file) {
[snip]
return Promise.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]);
});
我可能会继续使用
因为这被标记了 es6-promise
我想留下一个不使用扩展的 es6 答案:
var xyz = () => new Promise((r, e) => Resto.find({recommendation: {$gte: 0}},
err => err ? e(err) : r()))
.then(() => new Promise((r, e) =>
fs.readFile(__dirname + '/../../views/sitemap.jade',
err => err? e(err) : r())))
// [snip]
.then(() => Promise.all([
NLPromise('../m/sitemap.xml', map1),
NLPromise('../m/sitemap2.xml', map2)
]));
要避免的反模式:
- 嵌套。尽可能压平链条。狭义地使用
new Promise
。 - 隐藏错误。尽可能说出失败的真正原因。
- 扔绳子。抛出真正的错误对象,因为它们有堆栈跟踪。