6

关于这个 jsFiddle,我正在尝试动态添加一个在事件触发时创建的“延迟”,因此只有在解决所有延迟时才调用 done 回调,包括稍后添加的回调:

相关代码:

var promises = [ deferred1, ... ];
var p = when.all(promises).then(function() {
  console.log('All done!!');
  //! trigger
});

promises.push( deferredFromEvent ); // << ignored

更新:欢迎使用 Q 或 jQuery 的建议,我正在寻找一个可行的

4

4 回答 4

1

将您的固定承诺视为一个单独的捆绑包,而不是动态承诺。

等待捆绑的 Promise,然后通过初始空数组上的a管道它们的组合结果。$.when()您可以随时将新的 Promise 动态推送到该数组中。

http://jsfiddle.net/atesgoral/YVkVa/1/

HTML:

<p>You have 5 seconds to inject!</p>
<button id="inject">Inject Deferred 3</button>
<button id="resolve">Resolve Deferred 3</button>
<p>Deferred 1: <span id="dfd1">pending<span></p>
<p>Deferred 2: <span id="dfd2">pending<span></p>
<p>Deferred 3: <span id="dfd3">pending<span></p>
<h1 id="result"></h1>

JavaScript:

var dfd1 = $.Deferred(),
    dfd2 = $.Deferred(),
    fixed = [ dfd1.promise(), dfd2.promise() ],
    multiplexed = $.when.apply($, fixed), // Bundle the fixed ones
    reservoir = []; // Reservoir for dynamic promises

// The solution to your problem lies here. The rest is scaffolding.
var ultimate = multiplexed.pipe(function () {
    return $.when.apply($, reservoir);
});

ultimate.done(function() {
    $("#result").text("Done!");
});

window.setTimeout(function () {
    dfd1.resolve();
    $("#dfd1").text("resolved");
}, 2500);

window.setTimeout(function () {
    dfd2.resolve();
    $("#dfd2").text("resolved");
}, 5000);

var dfd3 = $.Deferred();

$("#inject").click(function () {
    reservoir.push(dfd3.promise());
});

$("#resolve").click(function () {
    dfd3.resolve();
    $("#dfd3").text("resolved");
});
于 2013-02-24T03:52:38.613 回答
0

“......所以只有在解决所有延迟时才调用完成的回调,包括后来添加的那些”没有意义,但我想我知道你的意思。

如果我理解正确,那么您想要可能被称为“可重新触发 when()”的东西 - 类似这样的东西(基于 jQuery):

function PromiseSet(memory, once) {//javascript Constructor
    var flags = [];
    if(memory) flags.push('memory');
    if(once) flags.push('once');
    var promises = [],
        doneCallbacks = $.Callbacks(flags.join(' ')),
        failCallbacks = $.Callbacks(flags.join(' '));
    this.add = function(promise, val) {
        promises.push(promise);
        if(val) { this.fire(val); }
        return this;
    };
    this.done = function(fn) {
        doneCallbacks.add(fn);
        return this;
    };
    this.fail = function(fn) {
        failCallbacks.add(fn);
        return this;
    };
    this.fire = function(val) {
        val = val || null;
        $.when.apply($, promises).then(
            function() { doneCallbacks.fire(val); },
            function() { failCallbacks.fire(val); }
        );
        return this;
    };
    return this;
}

未经测试

所有方法都返回 this 以使它们可链接。

如果我正确编写了构造函数,那么您可以通过将布尔值传递给new PromiseSet(). 对于您提议的用法,我认为您需要通过(true, false),但尝试其他设置以查看会发生什么。

样本序列:

var myPromiseSet = new PromiseSet(true, false);
myPromiseSet.add(promise1);
myPromiseSet.add(promise2).add(promise3);
myPromiseSet.done(myDoneFunction).fail(myFailFunction);
myPromiseSet.fire("foo");
myPromiseSet.add(promise4).fire();
于 2013-02-20T04:45:38.927 回答
0

看看这个解决方案。通过这种实现,您可以跟踪许多动态添加的 Promise。如果您使用的是 jQuery Deferred,您可以这样做。 jsFiddle

请注意,我使用了 _.every来自 lodash 库的方法,因此您也需要安装 lodash

function doAlotOfAsyncThings(callback) {
    var promises = [];
    var def = $.Deferred();
    console.log('Adding first promise');
    promises.push(def.promise());

    setTimeout(function() {
        // Dinamically adding second promise. Important note: last added promise must be added to array BEFORE previous promise has been resolved
        var def2 = $.Deferred();
        var def3 = $.Deferred();
        console.log('Dinamically adding second and third promises');
        promises.push(def2.promise());
        promises.push(def3.promise());
        console.log('Resolving first promise');
        def.resolve();

        setTimeout(function() {
            console.log('Resolving second and third promises');
            def2.resolve();
            def3.resolve();
        }, 1500);
    }, 1000);


    function checkAllDone() {
        console.log('Checking $.when');
        $.when.apply(null, promises).then(function() {
            // we have to check state of every promise in array
            var all_resolved = _.every(promises, function(elem) { return elem.state() == 'resolved'; });
            if(all_resolved) {
                console.log('All promises are resolved! callback();')
                callback();
            } else {
                console.log('Hm, seems that some promises were dinamically added, waiting for them..');
                checkAllDone();
            }
        });
    }
    checkAllDone();
}

doAlotOfAsyncThings(function(){
    console.log('Done');
});

使用 Q.js,它甚至更短。
在这个解决方案中,我使用_.any来自 lodash 库的方法

function doAlotOfAsyncThings(callback) {
    var promises = [];
    var def = Q.defer();
    console.log('Adding first promise');
    promises.push(def.promise);

    setTimeout(function() {
        // Dinamically adding second promise. Important note: last added promise must be added to array BEFORE previous promise has been resolved
        var def2 = Q.defer();
        var def3 = Q.defer();
        console.log('Dinamically adding second and third promises');
        promises.push(def2.promise);
        promises.push(def3.promise);
        console.log('Resolving first promise');
        def.resolve();

        setTimeout(function() {
            console.log('Resolving second and third promises');
            def2.resolve();
            def3.resolve();
        }, 1500);
    }, 1000);


    function checkAllDone() {
        console.log('Checking $.when');
        Q.all(promises).then(function() {
            // Q remove resolved promises from array so we have to check if array contains some non-undefined elements (those elements would be non-resolved dinamically added promises)
            if(_.any(promises)) {
                console.log('Hm, seems that some promises were dinamically added, waiting for them..');
                checkAllDone();
            } else {
                console.log('All promises are resolved! callback();')
                callback();
            }
        });
    }
    checkAllDone();
}

doAlotOfAsyncThings(function(){
    console.log('Done');
});
于 2014-12-23T09:32:49.017 回答
0

预先创建您知道需要的所有承诺。与他们建立一个.when。保存从.when. 当您添加需要新承诺的新事件时,请.when使用之前的承诺添加新的.whens,以及您已完成的任何新承诺。

single points of failure如果您使用 final.when作为“继续使用应用程序” ,您的应用程序将有多个。IE:如果任何一个承诺在任何时候失败,那么之后.when创建的任何承诺也将失败。

...但是如果这是您的意图,或者您有一些可靠的错误处理,那么您应该这样做。

我试图让这个库与库无关——通常,我使用自己的实现,它介于 jQuery 所做的事情和 Crockford 在最近一次谈话中所做的事情之间,但如果我们假设:

函数返回promise-handlers
“当”返回promise-handler promise-handlers 至少有一个.doneand .fail-- 或者接受两个参数,或者函数内部发生的任何事情和任何事情都将控制promise是rejected/resolvedor kept/broken(或其他),然后你最终可能会得到一堆看起来像这样的功能:

var doing = doSomething(),     // returns promise
    making = makeSomething(),  // returns promise
    loading = loadSomething(), // returns promise


    dependencies_lvl_1 = when(doing, making, loading);

稍后,您可能会添加一个新模块或小部件——也许它会节省一些工作:

var saving = saveSomething(), //returns promise
    dependencies_lvl_2 = when(dependencies_lvl_1, saving);

也许在那之后,您需要切换页面,但是您需要先缓存数据

var caching = cacheData(),   // returns promise
    when(dependencies_lvl_2, caching)
        .done(goToNextPage)
        .fail(handleError);

如果您查看它,您就会知道一个事实,即只要when返回一个只有在所有承诺都被遵守(并且当所有承诺都被遵守时)才会成功的承诺,并且它们都没有破坏,那么dependencies_lvl_2就包括来自 的所有依赖项dependencies_lvl_1,加上额外的承诺。

那么, level-3.when的分辨率取决于添加到链中的每一件事。

只要你不断地将你的承诺缓存到变量中(或某种未来的访问),你就可以继续将它们链接在一起,成为永恒。

于 2013-02-19T17:59:02.210 回答