用另一个Promise履行(不解决)Promise

IT技术 javascript ecmascript-6 es6-promise
2021-02-09 16:11:35

我想用一些其他的Promise来兑现一个Promise。关键是我真的想在第一个Promise完成后立即访问(仍在等待)第二个Promise不幸的是,我似乎只能在两个Promise都实现后才能获得第二个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一旦用户选择了一个文件,它就会返回一个Promise此时,我们可能仍然需要通过网络下载所选文件。因此,我无法picker将所选文件作为分辨率值来实现。取而代之的是,picker应该用另一个Promise来实现,downloadProgress,这反过来最终会用选定的文件来实现。

为了完整起见,这是该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;
}

在标线的问题是,我想履行Promisep1新的Promisep2,但我只知道如何解决它。履行和解决之间区别在于,解决首先检查提供的值p2是否再次是一个Promise。如果是,则执行p1将被推迟,直到p2被执行,然后p1将执行p2的分辨率值而不是p2它本身。

我可以通过围绕 构建一个包装器来解决这个问题p2,即通过替换该行

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

来自第二个代码示例

        resolveP1({promise: p2});

然后,在第一个代码示例中,我必须替换该行

        return downloadProgress;

经过

        return downloadProgress.promise;

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

我很感激任何建议。

3个回答

除了我在问题中已经描述的解决方法之外,似乎没有其他解决方案。为了将来参考,如果您想实现(而不是解析)一个p带有 value 的promise,另一个 promiseval在哪里val,那么仅调用pwith 参数的 promise 解析函数val将无法按预期工作。它会导致p被“锁定”在 的状态上val,这样一旦完成p就会用val的分辨率值val来完成(参见规范)。

相反,包装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是一个Promise此解决方案有效如果您不能对val的类型做出任何假设,那么您似乎不走运。要么您必须始终将Promise解析值包装在另一个对象中,要么您可以尝试检测是否val具有then“函数”类型的字段并有条件地包装它。

也就是说,在某些情况下,promise 解析的默认行为实际上可能会产生预期的效果。因此,仅当您确定要履行而不是使用第二个Promise解决第一个Promise时才使用上述解决方法

在接受自己的答案之前等待更多答案可能是一个更好的主意。
2021-03-27 16:11:35
这个问答应该得到更多的支持。这是 nativePromise一个巨大缺点幸运的是,补丁很容易。
2021-04-05 16:11:35

尽管不同的人使用不同的术语,但在通用术语中,“履行”意味着将Promise置于“成功”状态(与“拒绝”相反)——该状态将触发然后then处理程序挂起它。

换句话说,你不能用Promise来“履行”Promise。你可以用一个值来实现它。(顺便说一句,术语“解决”通常意味着实现或拒绝。)

您可以做的是从.then处理程序返回一个Promise,这将具有用返回的Promise基本上替换原始Promise的效果。

这是一个简单的例子:

asyncTask1 . then(asyncTask2) . then(processData)

whereasyncTask1是一个promise,asyncTask2是一个返回promise的函数。所以当asyncTask1完成(done成功),然后asyncTask2运行,.then由promiseasyncTask2返回的promise返回的promise ,这样当它完成时,可以处理数据。

我可以通过Promise.resolve使用 promise 作为参数调用来做类似的事情这有点用词不当,因为我没有解决技术意义上的Promise。而是新建的promise被我传入的promise“占据”了,也是没用的,因为使用结果和使用我传入的promise完全一样:

Promise.resolve(asyncTask2)

行为完全相同

asyncTask2

(假设 asyncTask2 已经是一个promise;否则Promise.resolve会创建一个promise,它会立即通过传入的值实现。)

正如您可以将Promise传递给Promise.resolve,您可以将Promise传递给resolve提供给您函数,作为Promise构造函数回调的参数。如果您传递给的参数resolve是非Promise的,则Promise会立即使用该值实现。但是,如果您传递给的参数resolve是另一个Promise,则该Promise“接管了您正在构建的Promise的主体”。换句话说,您正在构建的Promise开始的行为与传递给 的Promise完全相同resolve

“行为准确”我的意思是,如果您传入的Promiseresolve已经实现,那么您正在构建的Promise会立即以相同的值实现。如果您传入的Promiseresolve已经被拒绝,那么您正在构建的Promise会立即以同样的原因被拒绝。如果您传递给的Promiseresolve尚未解析,那么then当您传递给的Promiseresolve已解析时,您挂起正在构建的Promise的任何处理程序都将被调用

正如它是混淆的是Promise.resolve可能导致实际不解决的Promise,它也同样令人困惑的是调用resolve交给你作为参数传递给Promise的构造可能功能不是,如果你有一个悬而未决的调用它实际上解决的Promise正在建设中Promise。相反,正如我现在所说的那样,它的作用是将正在构建的Promise置于与传递给 的Promise完全一致的状态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');
    });
}

我不是很清楚你的问题,所以这可能不是你想要的。如果您愿意,请澄清。

我认为 OP 确实非常了解解决和实现之间的区别。他确实想用一个Promise来实现,因为Promise只是一个value。
2021-03-17 16:11:35
您的实现的问题是该文件在用户选择后可能无法立即使用。如果仍然需要下载,那么我希望由 启动和监控下载pickFile,但我仍然希望履行pickFile用户选择文件后立即返回的Promise(下载应在后台继续)。
2021-03-23 16:11:35
我意识到,通俗地说,“履行”和“解决”Promise有时被用作同义词。我使用的是ES6 规范意义上的术语,这明确区分了两者。从这个意义上说,该resolve函数的命名是正确的,正如规范所述:“如果一个 promise 被解决,或者它已被‘锁定’以匹配另一个 promise 的状态,那么它就会被解析。” (其中“已解决”的意思是“完成或拒绝”。)
2021-04-06 16:11:35

在从 Angular 的 $q 迁移到原生的 Promise 特性的过程中找到了类似的解决方案。Promise.all可能是一种选择(在独立并行异步任务的情况下),通过传递适当的对象或用状态装饰的东西,在适当的时候将其传递给任何准备好的东西。在下面的 Promise.all 示例中,请注意它如何在其中一个Promise中恢复——我花了一段时间才意识到如何重定向链的结果。all 的结果只是最后一个 promise 的返回。虽然这并不能回答问题的标题,但使用return Promise.reject(<an-object-including-a-promise>)(或解决)会在此过程中提供一系列和/或一组异步任务共享访问和控制。在选择,下载然后使用文件的情况下,我会取出进度事件处理然后执行:pickFile.then(download,orFailGracefully)downloadProgressdownloadonResolve 处理程序(下载进度似乎不是异步任务)。下面是控制台中的相关实验。

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;
});
我可以建议避免延迟反模式,还是这是您的解决方案的有意部分?
2021-04-01 16:11:35
有用的东西@Bergi。编辑之前:问题不清楚,但似乎是“如何解决(或拒绝)具有另一个Promise或对象的Promise”,这就是我的回答试图解决的问题。示例代码似乎不是解决有些模糊问题的最佳方法,即将进度事件与异步请求/Promise混合对我来说没有意义。最好删除答案还是只是从 Promise.all 调整到 promise.then.then 链(或其他东西?)?谢谢!
2021-04-04 16:11:35