更复杂的延迟/承诺使用和链只是't单击.希望能澄清一下
More complex Deferred/promise usage and chains just aren't clicking. Would love some clarification
我发现各种递延/承诺库的文档通常涵盖非常简单的用例,例如链接1或2个函数/方法,并导致解决或拒绝的成功或错误。
然而,当涉及到更复杂的用例(5+函数/方法链;嵌套的Deferred;从其他Deferred中返回promise)时,我会两手空空,变得非常沮丧。
例如,我有一个包含2个嵌套函数的函数。每个子函数都返回一个promise,我希望父函数返回子函数的成功/失败结果:
var saveUser = function(user) {
var saveNewUser = function(user) {
var deferred = when.defer();
user.save(function (err) {
if (err) {
deferred.reject();
}
else {
// forcing a rejection here for testing purposes
deferred.reject(6);
}
});
return deferred.promise;
}
var supplyUserCollections = function() {
var deferred = when.defer();
deferred.reject();
return deferred.promise;
}
return saveNewUser(user).then(supplyUserCollections(), function() {
console.log('failed to save new user');
}).then(function(data) {
console.log('succeeded to do everything');
}, function() {
console.log('failed to seed new user collections');
});
}
这不起作用;奇怪的是,"成功地做了所有事情"console.log
会触发,尽管我正在强制两个子函数拒绝/失败。鉴于.then
上的第一个参数是成功的案例,我真的不明白为什么会发生这种情况。此外,假设父函数saveUser
是另一个广泛承诺链的一部分,例如:
dropExistingCollections(collections).then(saveEntities(albums), function() {
console.log('failed to drop existing collections');
}).then(saveEntities(movies), function() {
console.log('failed to save all albums');
}).then(saveEntities(games), function() {
console.log('failed to save all movies');
}).then(saveUsers(users), function() {
console.log('failed to save all games');
}).then(function(data) {
console.log(data);
console.log('successfully saved all seed data');
res.send('database data wiped and re-seeded');
}, function() {
console.log('failed to save all users');
});
我真的不知道如何正确地从saveUser
返回promise,使其与其他函数可链接,这些函数都是返回已解析/已拒绝Deferred的简单函数。
我真的很想更多地澄清这些更复杂的Deferreds/promise用例应该如何处理。这显然是一个非常密集的话题,我发现的大多数材料都没有引起我的共鸣
始终从同步代码开始。它更容易理解,而且转换为异步代码从来没有那么困难。如果你包含了如何编写同步代码,而不仅仅是一些稍微损坏的异步代码,我会更容易理解你想要代码做什么
简短回答
您最初的问题可能只是在无意的地方添加了括号。您应该始终将函数传递给then
处理程序:
return saveNewUser(user).then(supplyUserCollections /* NOTE: no parenthesis */, function() {
console.log('failed to save new user');
}).then(function(data) {
console.log('succeeded to do everything');
}, function() {
console.log('failed to seed new user collections');
});
这似乎也是你对第二个函数的主要误解。
答案很长
想象一下,您想要编写的代码可能如下所示(如果用户上有saveSync
方法和save
方法)。
function saveNewUser(user) {
var result = user.saveSync();//may throw
// forcing a throw here for testing purposes
throw 6;
}
function supplyUserCollections() {
throw new Error('failed supplyUserCollections');
}
function saveUser(user) {
try {
saveNewUser(user);
} catch (ex) {
console.log('failed to save new user');
return;
}
try {
supplyUserCollections();
} catch (ex) {
console.log('failed to seed new user collections');
return;
}
console.log('succeeded to do everything');
}
现在我们可以非常简单地转换saveNewUser
和supplyUserCollections
:
function saveNewUser(user) {
var deferred = when.defer();
user.save(function (err) {
if (err) {
deferred.reject();
}
else {
// forcing a rejection here for testing purposes
deferred.reject(6);
}
});
return deferred.promise;
}
function supplyUserCollections() {
var deferred = when.defer();
deferred.reject();
return deferred.promise;
}
这样,我们只需要转换更复杂的saveUser
方法:
function saveUser(user) {
saveNewUser(user)
.then(function () {
//this only happens if `saveNewUser` succeeded, like the bit after
//a catch block containing a `return`
return supplyUserCollections()
.then(function () {
console.log('succeeded to do everything');
}, function (ex) {
console.log('failed to seed new user collections');
})
}, function (ex) {
//this happens when `saveNewUser` failed (just like the catch block)
console.log('failed to save new user');
})
}
现在,在我看来,同步函数的一个更可能的版本可能是这样的:
function saveUser(user) {
saveNewUser(user);
supplyUserCollections();
console.log('succeeded to do everything');
}
你可以写为:
function saveUser(user) {
saveNewUser(user)
.then(function () {
//this only happens if `saveNewUser` succeeded
return supplyUserCollections()
})
.then(function () {
//this happens when both methods succeeded
console.log('succeeded to do everything');
})
}
如果您修复了括号问题(Forbes指出),那么您的第一个示例应该输出:
failed to save new user
succeeded to do everything
所以现在你可能会问,为什么它显示在第一步失败时成功地完成了所有事情。是的,因为在你的流程中,你超越了最初的错误,回到了成功的轨道上。为了保持错误状态,您需要在回调中重新抛出相同的错误:
saveNewUser(user).then(supplyUserCollections(), function(err) {
console.log('failed to save new user');
throw err; // Re-throw to maintain error state
})
有了这个,你应该看到:
failed to save new user
failed to seed new user collections
不过,你的流量与最佳实践并不相似。第一件事:您应该清楚地知道您的流在哪一步崩溃,仅仅是因为错误消息的内容,而不应该在每一步都侦听错误,因为它已经过时并且过于冗长。
假设saveNewUser
可能会因无法保存用户错误而崩溃,supplyUserCollections
可能会因无法播种新用户集合而崩溃。你的流程应该很简单:
saveNewUser(user).then(supplyUserCollections).done(function () {
console.log("Success!");
}, function (err) {
console.error(err.message);
});
然后,如果任何一个步骤崩溃,您将看到相应的消息,这就是您应该如何有效地使用promise。在大多数情况下,您只需要在链的末尾处理一次错误。
- 希望在单击href链接时显示我的广告
- 一个按钮中的两个onClick功能,并希望一个接一个地发生一个单击功能
- 我希望每次单击按钮时都能将每张图片更改为显示在屏幕上的同一位置
- 如果单击某个按钮,我希望我的onBlur处理程序能做一些不同的事情
- 当再次单击时,我希望颜色消失.(喜欢/讨厌按钮,一切都可以,但如果我点击两次喜欢按钮,我希望颜色消失)
- 希望在网页上显示的 .obj 文件上有单击事件
- (飞镖)我希望触发两个不同的onclick功能,具体取决于单击时是否选中了单选按钮.我该怎么做
- 我不希望弹出窗口在用户单击离开它时最小化
- 当我单击菜单元素时,希望用href在当前页面中打开一个页面
- 我希望根据使用jquery单击的选项卡来显示选项卡内容
- 单击单选按钮时,希望使用jquery在下拉列表中输出拆分的值
- 这只会删除表中的第一行.我希望它删除我使用jQuery和html单击的任何行
- 使用$(“[property=value]”).click(),我希望两次单击之间有一个延迟
- 更复杂的延迟/承诺使用和链只是't单击.希望能澄清一下
- 我希望在单击菜单时隐藏其他菜单项
- 我有表行选择向上和向下的箭头,但希望能够单击它们
- 单击删除按钮后,希望取消选中复选框
- 在Html文件中的值显示上设置Click事件,并希望在单击它时显示子元素
- 使用jQuery发布表单,并希望在单击后更改提交按钮
- 网页上有不可见的表单,并希望在使用 DojoToolkit 单击按钮时使它们可见,不确定我的代码有什么问题