简体   繁体   English

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

[英]Fulfill (don't resolve) promise with another promise

I want to fulfill a promise with some other promise. 我想兑现一个诺言。 The point is that I really want to get access to the (still pending) second promise as soon as the first promise is fulfilled. 关键是我真的想在第一个诺言兑现后立即访问(仍在等待中)第二个诺言 Unfortunately, I only seem to be able to get the second promise's resolution value once both both promises are fulfilled. 不幸的是,当两个诺言都实现时,我似乎只能获得第二个诺言的解决值。

Here's the use case that I have in mind: 这是我要记住的用例:

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.
    }
)

The function pickFile displays a file picker where the user may pick a file either from their own hard drive or from a URL. pickFile函数显示文件选择器,用户可以在其中从自己的硬盘驱动器或URL中选择文件。 It returns a promise picker that is fulfilled as soon as the user has picked a file. 它返回一个诺言picker ,该picker在用户选择文件后立即完成。 At this point, we may still have to download the selected file over the network. 此时,我们可能仍然必须通过网络下载所选文件。 Therefore, I cannot fulfill picker with the selected file as resolution value. 因此,我无法将所选文件作为分辨率值来实现picker Instead, picker should be fulfilled with another promise, downloadProgress , which in turn will eventually be fulfilled with the selected file. 取而代之的是, picker还应满足另一个承诺downloadProgress ,而该承诺最终将与所选文件一起实现。

For completenes, here's a mock implementation of the pickFile function: 对于完整性,这是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;
}

The problem in the marked line is that I would like to fulfill the promise p1 with the new promise p2 , but I only know how to resolve it. 标记行中的问题是我想用新的诺言p2实现诺言p1 ,但是我只知道如何解决它。 The difference between fulfilling and resolving is that resolving first checks if the supplied value p2 is again a promise. 满足与解决之间区别在于 ,解决首先检查提供的值p2是否再次是一个承诺。 If it is, then fulfillment of p1 will be deferred until p2 is fulfilld, and then p1 will be fulfilled with p2 's resolution value instead of p2 itself. 如果是,则将p1实现推迟到p2满足,然后使用p2的分辨率值而不是p2本身来实现p1

I could work around this issue by building a wrapper around p2 , ie by replacing the line 我可以通过围绕p2构建包装器来解决此问题,即替换行

        resolveP1(p2);  // <--- PROBLEM: I actually want to *fulfill* p1 with p2

from the second code example by 从第二个代码示例

        resolveP1({promise: p2});

Then, in the first code example, I'd have to replace the line 然后,在第一个代码示例中,我将不得不替换该行

        return downloadProgress;

by 通过

        return downloadProgress.promise;

But this seems like a bit of a hack when all I really want to do is just fulfill (instead of resolve) a promise. 但是,当我真正想做的只是兑现(而不是解决)承诺时,这似乎有点不合时宜。

I'd appreciate any suggestions. 我将不胜感激任何建议。

There doesn't seem to be a solution apart from the workaround I already described in the question. 除了我已经在问题中描述的解决方法之外,似乎没有其他解决方案。 For future reference, if you want to fulfill (rather than resolve) a promise p with a value val , where val is another promise, then just calling the promise resolution function for p with argument val won't work as expected. 供将来参考,如果您想实现(而不是解决)带有值val的promise p ,其中val是另一个promise,那么仅使用参数val调用p的promise解析函数将无法按预期工作。 It would cause p to be "locked in" on the state of val , such that p will be fulfilled with val 's resolution value once val is fulfilled (see spec ). 这将导致p被“锁定”在val的状态上,这样一旦val满足, p将以val的分辨率值实现(请参见spec )。

Instead, wrap val in another object and resolve p with that object: 而是将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...
});

This solution works if you already know that val is a promise. 如果您已经知道val是一个承诺,则此解决方案有效。 If you cannot make any assumptions about val 's type, then you seem to be out of luck. 如果您不能对val的类型做任何假设,那么您似乎不走运。 Either you have to always wrap promise resolution values in another object, or you can try to detect whether val has a field then of type "function" and wrap it conditionally. 要么你必须在另一个对象总是包裹承诺分辨率值,或者你可以尝试检测是否val有一个字段then类型的“功能”,并有条件地包裹。

That said, in some cases the default behavior of promise resolution may actually have the desired effect. 也就是说,在某些情况下,承诺解决方案的默认行为实际上可能会达到预期的效果。 So only use the workaround described above if you are sure that you want to fulfill instead of resolve the first promise with the second one. 因此,仅当您确定要实现而不是用第二个承诺来解决第一个承诺时,才使用上述解决方法。

Although different people use different terms, in common terminology, "fulfill" means to put a promise in the "success" state (as opposed to "reject")--the state that will trigger then then handlers hanging off it. 尽管不同的人使用不同的术语,但在通用术语中,“实现”是指将承诺置于“成功”状态(与“拒绝”相对),该状态将触发then处理程序将其挂起。

In other words, you cannot "fulfill" a promise with a promise. 换句话说,您不能用承诺“履行”承诺。 You can fulfill it with a value. 您可以实现自己的价值。 (By the way, the term "resolve" is usually meant as either of fulfilling or rejecting.) (顺便说一句,“解决”一词通常指实现或拒绝。)

What you can do is return a promise from a .then handler and that will have the effect of essentially replacing the original promise with the returned promise. 您可以做的是从.then处理程序返回一个诺言,这实际上将用返回的诺言替换原始诺言。

Here is a simple example of doing that: 这是一个简单的例子:

asyncTask1 . then(asyncTask2) . then(processData)

where asyncTask1 is a promise, and asyncTask2 is a function which returns a promise. 其中asyncTask1是一个承诺,而asyncTask2是一个返回一个承诺的函数。 So when asyncTask1 is fulfilled (done successfully), then asyncTask2 runs, and the promise returned by the .then is "taken over" by the promise asyncTask2 returns, so that when it finishes, the data can be processed. 所以,当asyncTask1满足(成功完成),然后asyncTask2运行,并通过返回的承诺.then被“接管”的承诺asyncTask2回报,这样,当它结束时,数据可以被处理。

I can do something similar by calling Promise.resolve with a promise as parameter. 我可以通过以Promise.resolve作为参数调用Promise.resolve来做类似的事情。 It's a bit of a misnomer, because I'm not resolving the promise in the technical sense. 这有点用词不当,因为我没有从技术角度解决诺言。 Instead, the new promise created is "inhabited" by the promise I passed in. It's also useless, because using the result is exactly the same as using the promise I passed in: 相反,创建的新承诺被我传入的承诺“占用”。它也没有用,因为使用结果与使用我传入的承诺完全相同:

Promise.resolve(asyncTask2)

behaves exactly the same as 行为与

asyncTask2

(assuming asyncTask2 is already a promise; otherwise Promise.resolve has the effect of creating a promise which is immediately fulfilled with the passed in value.) (假设asyncTask2已经是一个承诺;否则Promise.resolve可以创建一个保证,并立即通过传入的值来实现。)

Just as you can pass a promise to Promise.resolve , you can pass a promise to the resolve function provided to you as a parameter of the promise constructor callback. 就像可以将Promise.resolve传递给Promise.resolve ,您也可以将Promise.resolve传递给作为Promise.resolve构造函数回调的参数提供给您的resolve函数。 If the parameter you pass to resolve is a non-promise, the promise immediately fulfills with that value. 如果您传递来resolve的参数是非承诺,则promise将立即用该值兑现。 However, if the parameter you pass to resolve is another promise, that promise "takes over the body" of the promise you are constructing. 但是,如果传递给您resolve的参数是另一个promise,则该promise将“接管您正在构建的promise的主体”。 To put it another way, the promise you are constructing starts to behave exactly as the the promise passed to resolve . 换句话说,您正在构造的promise的行为与传递给resolve的promise的行为完全相同。

By "behave exactly" I mean, if the promise you pass in to resolve is already fulfilled, the promise you are constructing is instantly fulfilled with the same value. “精确地表现”是指,如果您传递来resolve的诺言已经兑现,那么您正在构造的诺言将立即以相同的价值兑现。 If the promise you pass in to resolve is already rejected, the promise you are constructing is instantly rejected with the same reason. 如果您传递来resolve的承诺已被拒绝,则您出于相同原因立即构造的承诺将被拒绝。 If the promise you pass in to resolve is not resolved yet, then any then handlers you hang off the promise you are constructing will be invoked if and when the promise you pass to resolve is resolved. 如果您在通过承诺resolve尚未解决,那么任何then处理你挂过你正在构造是否以及何时你通过的承诺将被调用的承诺resolve得到解决。

Just as it is confusing that Promise.resolve may result in a promise which is not actually resolved, it is similarly confusing that calling the resolve function handed to you as a parameter to the promise constructor may not actually resolve the promise being constructed if you call it with an unresolved promise. 正如它是混淆的是Promise.resolve可能导致实际上未解决的承诺,它也同样混乱,在调用的resolve交给你作为参数传递给许构造函数实际上可能没有解决的承诺,如果你调用正在建设中它有一个未解决的承诺。 Instead, as I've said a couple of times now, it has the effect of putting the promise being constructed in a state of total congruence with the promise passed to resolve . 相反,正如我现在已经说过几次,它的作用是使正在构建的承诺与通过resolve的承诺完全一致。

Therefore, unless I am missing the point of your question, pickfile could be written as 因此,除非我遗漏了您的问题, 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');
    });
}

I didn't really understand your question clearly, so this might not be what you want. 我不太清楚您的问题,因此可能不是您想要的。 Please clarify if you care to. 请说明您是否愿意。

Found a similar solution in the process of moving away from Angular's $q to the native Promise feature. 在从Angular的$ q转​​到本机Promise功能的过程中找到了类似的解决方案。 Promise.all could be an option (in cases of independent parallel async tasks) by passing around an appropriate object, or something decorated with the state, passing it off to whatever is ready when appropriate. Promise.all可能是一个选择(在独立并行异步任务的情况下),方法是传递适当的对象或带有状态修饰的对象,并在适当的时候传递给已准备好的对象。 In the Promise.all sample below note how it recovers in one of the promises--took me awhile to realize how to redirect the result of a chain. 在下面的Promise.all示例中,请注意它如何在承诺中恢复-让我花了一会儿意识到如何重定向链的结果。 The result of the all is just the last promise's return. 一切的结果只是最后诺言的回报。 While this doesn't answer the question's title, using return Promise.reject(<an-object-including-a-promise>) (or resolve) gives a series and/or group of async tasks shared access and control along the way. 尽管这不能回答问题的标题,但使用return Promise.reject(<an-object-including-a-promise>) (或解决)可在执行过程中提供一系列和/或一组异步任务共享访问和控制。 In the case of picking, downloading then working with a file I'd take out the progress-event handling then do: pickFile.then(download,orFailGracefully) with downloadProgress handled within the download onResolve handler (download-progress doesn't appear to be an async task). 在采摘,下载然后我拿出进度的事件处理则文件工作的情况下,这样做: pickFile.then(download,orFailGracefully)downloadProgress的内部处理download onResolve处理程序(下载进度不会出现是一个异步任务)。 Below are related experiments in the console. 以下是控制台中的相关实验。

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;
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM