履行(不要解决)承诺与另一个承诺

时间:2015-08-23 15:14:38

标签: javascript ecmascript-6 es6-promise

我希望履行承诺以及其他承诺。重点是,我真的希望在第一个承诺完成后立即访问(仍然未决的)第二个承诺。不幸的是,一旦两个承诺都得到满足,我似乎只能获得第二个承诺的分辨率值。

这是我想到的用例:

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;

但是,当我真正想做的只是履行(而不是解决)承诺时,这似乎有点像黑客。

我很感激任何建议。

3 个答案:

答案 0 :(得分:1)

除了我已经在问题中描述的解决方法之外,似乎没有解决方案。为了将来参考,如果您想要履行(而不是解决)具有值p的承诺val,其中val是另一个承诺,那么只需为{{调用承诺解析函数参数p的1}}将无法正常工作。这会导致valp状态中“锁定”,这样valp的{​​{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;
});