我希望履行承诺以及其他承诺。重点是,我真的希望在第一个承诺完成后立即访问(仍然未决的)第二个承诺。不幸的是,一旦两个承诺都得到满足,我似乎只能获得第二个承诺的分辨率值。
这是我想到的用例:
var picker = pickFile();
picker.then( // Wait for the user to pick a file.
function(downloadProgress) {
// The user picked a file. The file may not be available just yet (e.g.,
// if it has to be downloaded over the network) but we can already ask
// the user some more questions while the file is being obtained in the
// background.
...do some more user interaction...
return downloadProgress;
}
).then( // Wait for the download (if any) to complete.
function(file) {
// Do something with the file.
}
)
函数pickFile
显示文件选择器,用户可以从自己的硬盘驱动器或URL中选择文件。它返回一个用户选择文件后立即履行的承诺picker
。此时,我们可能仍需要通过网络下载所选文件。因此,我无法使用所选文件作为分辨率值来picker
。相反,picker
应该使用另一个承诺downloadProgress
来实现,最终将使用所选文件来履行承诺。
对于completenes,这里是pickFile
函数的模拟实现:
function pickFile() {
...display the file picker...
var resolveP1 = null;
var p1 = new Promise(
function(resolve, reject) {
resolveP1 = resolve;
}
);
// Mock code to pretend the user picked a file
window.setTimeout(function() {
var p2 = Promise.resolve('thefile');
resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2
}, 3000);
return p1;
}
标记行中的问题是,我希望履行承诺p1
并使用新承诺p2
,但我只知道如何解决它。如果提供的值p2
再次成为承诺,则difference between fulfilling and resolving解析首先检查。如果是,则p1
的完成将推迟到p2
完成,然后p1
将使用p2
的分辨率值而非{p2
来实现{1}}本身。
我可以通过在p2
周围构建一个包装来解决这个问题,即更换一行
resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2
的第二个代码示例中的
resolveP1({promise: p2});
然后,在第一个代码示例中,我必须替换行
return downloadProgress;
通过
return downloadProgress.promise;
但是,当我真正想做的只是履行(而不是解决)承诺时,这似乎有点像黑客。
我很感激任何建议。
答案 0 :(得分:1)
除了我已经在问题中描述的解决方法之外,似乎没有解决方案。为了将来参考,如果您想要履行(而不是解决)具有值p
的承诺val
,其中val
是另一个承诺,那么只需为{{调用承诺解析函数参数p
的1}}将无法正常工作。这会导致val
在p
状态中“锁定”,这样val
将p
的{{1}}分辨率值val
val
已完成(见spec)。
而是将val
包装在另一个对象中并使用该对象解析p
:
var resolveP; // Promise resolution function for p
var p = new Promise(
function(resolve, reject) {
resolveP = resolve;
}
);
function fulfillPwithPromise(val) { // Fulfills p with a promise val
resolveP({promise: val});
}
p.then(function(res) {
// Do something as soon as p is fulfilled...
return res.promise;
}).then(function(res) {
// Do something as soon as the second promise is fulfilled...
});
如果您已经知道val
是承诺,则此解决方案有效。如果你不能对val
的类型做任何假设,那么你似乎运气不好。您必须始终将承诺解析值包装在另一个对象中,或者您可以尝试检测val
是否具有类型为“function”的字段then
并有条件地进行包装。
也就是说,在某些情况下,承诺解析的默认行为实际上可能具有所需的效果。因此,如果您确定要履行而不是解决第一个承诺与第二个承诺,请仅使用上述解决方法。
答案 1 :(得分:0)
虽然不同的人使用不同的术语,但在通用术语中,“履行”意味着将承诺置于“成功”状态(而不是“拒绝”) - 将触发然后then
处理程序的状态挂掉它。
换句话说,你不能“履行”承诺的承诺。你可以用一个值来实现它。 (顺便说一句,“解决”一词通常意味着履行或拒绝。)
您可以做的是从.then
处理程序返回一个承诺,这将产生基本上用返回的承诺替换原始承诺的效果。
这是一个简单的例子:
asyncTask1 . then(asyncTask2) . then(processData)
其中asyncTask1
是一个承诺,而asyncTask2
是一个返回承诺的函数。因此,当asyncTask1
完成(成功完成),然后asyncTask2
运行,.then
返回的承诺被承诺asyncTask2
“接管”返回,以便完成后,可以处理数据。
我可以通过使用promise作为参数调用Promise.resolve
来做类似的事情。这有点用词不当,因为我不在技术意义上解决了这个承诺。相反,创造的新承诺是由我传入的承诺“居住”。它也没用,因为使用结果与使用我传递的承诺完全相同:
Promise.resolve(asyncTask2)
表现与
完全相同asyncTask2
(假设asyncTask2已经是一个承诺;否则Promise.resolve
具有创建承诺的效果,该承诺会立即通过传入的值来实现。)
正如您可以将承诺传递给Promise.resolve
一样,您可以将承诺传递给作为promise构造函数回调的参数提供给您的resolve
函数。如果传递给resolve
的参数是非承诺,则承诺会立即满足该值。但是,如果传递给resolve
的参数是另一个承诺,则该承诺将“接管您正在构建的承诺的正文”。换句话说,您构建的承诺开始的行为与传递给resolve
的承诺完全相同。
通过“完全表现”,我的意思是,如果您传递给resolve
的承诺已经履行,您正在构建的承诺将立即以相同的值实现。如果您传递给resolve
的承诺已被拒绝,则您正在构建的承诺会立即被拒绝,理由相同。如果您传递给resolve
的承诺尚未解决,那么当您传递给then
的承诺时,您将挂起您正在构建的承诺的任何resolve
处理程序解决。
同样令人困惑的是Promise.resolve
可能导致未实际解决的承诺,同样令人困惑的是调用作为promise构造函数的参数传递给您的resolve
函数可能< strong> not 实际上解决了正在构建的承诺,如果您使用未解决的承诺调用它。相反,正如我现在已经说过几次,它的作用是将承诺构建在与resolve
传递的承诺完全一致的状态中。
因此,除非我忽略了您的问题,否则pickfile
可以写成
function pickFile() {
return new Promise(function(resolve, reject) {
...display the file picker...
// Mock code to pretend the user picked a file
window.setTimeout(function() {
resolve('thefile');
});
}
我没有清楚地理解你的问题,所以这可能不是你想要的。如果你愿意,请澄清。
答案 2 :(得分:0)
在从Angular的$ q转移到本机Promise功能的过程中找到了类似的解决方案。 Promise.all
可以是一个选项(在独立并行异步任务的情况下),通过传递适当的对象,或者用状态装饰的东西,在适当的时候将其传递给任何准备好的东西。在下面的Promise.all示例中,请注意它如何在其中一个承诺中恢复 - 花了我一段时间才意识到如何重定向链的结果。这一切的结果只是最后的承诺的回归。虽然这并没有回答问题的标题,但使用return Promise.reject(<an-object-including-a-promise>)
(或解决方案)会在此过程中提供一系列和/或一组异步任务共享访问和控制。在选择,下载然后使用文件的情况下,我会取出进度事件处理,然后执行:pickFile.then(download,orFailGracefully)
downloadProgress
onResolve处理程序中处理download
(下载-progress似乎不是异步任务)。以下是控制台中的相关实验。
var q = {
defer: function _defer(){
var deferred = { };
deferred.promise = new Promise(function(resolve, reject){
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
};
var communityThatCares = q.defer();
communityThatCares.promise.then(function(someGood){
console.log('someGood', someGood);
return someGood;
}, function(someBad){
console.warn('someBad', someBad);
return someBad;
});
(new Promise(function(resolve, reject){ communityThatCares.about = 'communityThatCares'; setTimeout(resolve,1000, communityThatCares); }))
.then(
function(e){
console.log(3,e); return e.resolve(e);
}, function(e){
console.warn(3, e); return e.reject(e);
});
var todo = {
find:'swan'
};
var greaterGood = [(
(new Promise(function(res,rej){ res(todo); })).then(function(e){ e.stuff = 'things'; return e; }),
(new Promise(function(res,reject){
reject(todo);
})).then(function(e){ return e; }
,function(e){
console.warn(1,e);
e.recover = 'uh oh';
return Promise.resolve(e);
}).then(function(e){ console.log(2,e); return e; }),
(new Promise(function(res,rej){ res(todo); })).then(function(e){ console.log(1,e); e.schedule = 'today'; return e; },function(e){ console.warn(1,e); return e; }).then(function(e){ console.log(2,e); return e; }))
];
var nkay = Promise.all( greaterGood )
.then(function(todo){
console.log('all',todo[0]); return todo;
}, function(todo){
console.warn('all',todo[0]); return todo;
});