层层叠叠的承诺
Cascading promises
除了一个简单的承诺之外,任何事情通常都会让我感到困惑。 在这种情况下,我需要在 N 个对象上连续执行 2 次异步调用。 首先,我需要从磁盘加载文件,然后将该文件上传到邮件服务器。 我更喜欢同时执行这两个操作,但是我已经通过首先执行所有读取,然后执行所有上传来使其正常工作。 下面的代码有效,但我忍不住认为它可以做得更好。 我不明白的一件事是为什么 when.all 不拒绝。 我对文档的解释似乎暗示,如果其中一个承诺被拒绝,.all 将拒绝。 为了测试错误,我已经注释掉了较低的分辨率。 没有错误,事情似乎工作正常且有意义。
mail_sendOne({
from: 'greg@',
to: 'wilma@',
subject: 'F&B data',
attachments: [
{name: 'fred.html', path: '/fred.html'},
{name: 'barney.html', path: '/barney.html'}
]
})
.done(
function(res) {
console.log(res)
},
function(err) {
console.log('error ', err);
}
)
function mail_sendOne(kwargs) {
var d = when.defer();
var promises = [], uploadIDs = [], errs = [];
// loop through each attachment
for (var f=0,att; f < kwargs.attachments.length; f++) {
att = kwargs.attachments[f];
// read the attachment from disk
promises.push(readFile(att.path)
.then(
function(content) {
// upload attachment to mail server
return uploadAttachment({file: att.name, content: content})
.then(
function(id) {
// get back file ID from mail server
uploadIDs.push(id)
},
function(err) {
errs.push(err)
}
)
},
function(err) {
errs.push(err)
}
))
}
// why doesn't this reject?
when.all(promises)
.then(
function(res) {
if (errs.length == 0) {
kwargs.attachments = uploadIDs.join(';');
sendEmail(kwargs)
.done(
function(res) {
d.resolve(res);
},
function(err) {
d.reject(err);
}
)
}
else {
d.reject(errs.join(','))
}
}
)
return d.promise;
}
function readFile(path) {
var d = when.defer();
var files = {
'/fred.html': 'Fred Content',
'/barney.html': 'Barney Content'
}
setTimeout(function() {
d.reject('Read error');
//d.resolve(files[path]);
}, 10);
return d.promise;
}
function uploadAttachment(obj) {
var d = when.defer();
setTimeout(function() {
d.reject('Upload error');
//d.resolve(new Date().valueOf());
}, 10);
return d.promise;
}
function sendEmail(kwargs) {
var d = when.defer();
setTimeout(function(){
console.log('sending ', kwargs)
}, 5);
return d.promise;
}
反模式使代码比需要的更混乱。一种是在.then
回调中使用.then
,而您应该将它们链接起来。另一个是延迟反模式。
首先,让我们分别创建一个用于读取和上传的函数,这两个函数都处理各自的错误,并抛出一个具有更多上下文的新错误:
function readAndHandle(att) {
return readFile(att.path)
.catch(function (error) {
throw new Error("Error encountered when reading " + att.path + error);
});
}
function uploadAndHandle(att, content) {
return uploadAttachment({file: att.name, content: content})
.catch(function (error) {
throw new Error("Error encountered when uploading " + att.path + error);
});
}
然后,让我们将这两者组合成一个函数,该函数首先读取文件,然后上传文件。此函数返回一个承诺:
// returns a promise for an uploaded file ID
function readAndUpload(att) {
return readAndHandle(att)
.then(function (content) {
return uploadAndHandle(att, content);
});
}
现在,您可以使用.map()
将附件数组映射到文件 ID 的承诺数组:
var uploadedIdsPromise = kwargs.attachments.map(readAndUploadAsync);
这就是你可以传递到when.all()
.对此.then
处理程序会将一个 ID 数组传递给其回调:
return when.all(uploadedIdsPromise)
.then(function (ids) {
kwargs.attachments = ids.join(";");
return sendEmail(kwargs);
})
.catch(function (error) {
// handle error
});
这几乎就是它的要点。
这里需要注意的一件大事是,除了一个修改kwargs
变量的地方之外,承诺没有修改承诺链之外的任何内容。这有助于保持逻辑的简洁和模块化。
延迟反模式
请注意,上面的代码中没有 d.resolve
s 或 d.reject
s。您唯一应该使用 deferred
s 的情况是当您还没有可用的承诺时(或其他一些特殊情况下(。即便如此,如今也有首选的方式来创造承诺。
什么时候.js API 文档是这样说的:
注意:不鼓励使用 when.defer。在大多数情况下,使用 when.promise 、when.try 或 when.lift 可以更好地分离关注点。
当前推荐的从某些非承诺异步 API 创建承诺的方法是使用揭示构造函数模式。以您的uploadAttachment()
函数为例,它看起来像这样:
function uploadAttachment(obj) {
return when.promise(function (resolve, reject) {
setTimeout(function() {
resolve(new Date().valueOf());
// or reject("Upload error");
}, 10);
});
}
这就是 ES6 promise API 的工作方式,它使您不必在deferred
对象周围乱七八糟。
- 我的职位回报太快了,如何做出承诺
- 打破承诺链的好方法是什么
- 从函数返回角度承诺
- 我怎样才能获得承诺的价值
- 延期承诺值未更新/解析/延期
- 在承诺链中处理早期回报的最佳方式
- 承诺在非节点式回调上使用Bluebird
- 简单的ES6承诺问题-交换解决和拒绝参数
- 组合承诺和非承诺值
- 带有对象/原型的链式承诺(Q延期)
- AngularJS$q承诺使用socket.io
- React JS:未捕获(在承诺中)语法错误:在位置 0 的 JSON 中意外<令牌
- 当一些承诺失败时,如何继续使用$q.all()
- Nodejs和express路由,如何处理客户端的承诺
- 如何在多承诺链中处理谷歌地图API V3事件
- 承诺合并流
- JavaScript承诺-无法读取属性'那么'的未定义
- 等待循环调用的所有承诺完成
- Pg承诺性能提升:在冲突中
- 层层叠叠的承诺