如何从这个jsFiddle中动态添加延迟到promise?

时间:2013-02-19 16:41:07

标签: javascript events deferred promise when-js

关于this jsFiddle,我试图动态添加在事件触发时创建的“延迟”,因此仅在解析所有延迟时调用完成的回调,包括稍后添加的那些:

相关代码:

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

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

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

4 个答案:

答案 0 :(得分:1)

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

等待捆绑的promises,然后在最初为空的数组中通过$.when() pipe结合它们的结果。您可以随时动态地将新的promises推送到此数组中。

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

答案 1 :(得分:0)

预先创建您知道自己需要的所有承诺。用它们构建.when。保存从.when返回的承诺。当您添加需要新承诺的新事件时,请使用之前.when的承诺添加新的.when,以及您已完成的任何新承诺。

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

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

我正在努力保持这个与库不相关的东西 - 通常,我使用自己的实现,这是jQuery所做的事情之间的中途,以及Crockford在最近的一次谈话中所做的事情,但是如果我们假设: / p>

函数返回promise-handlers
“when”返回一个promise-handler promise-handlers至少有一个.done.fail - 或者接受两个参数,或者其他什么 并且在函数内部发生的任何事情都将控制承诺是rejected/resolved还是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的所有依赖项,以及其他承诺。

然后,等级3 .when的分辨率取决于已添加到链中的每一件事物。

只要你将你的承诺缓存到变量(或某种未来访问)中,你就可以将它们连在一起,变成永恒。

答案 2 :(得分:0)

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

如果我理解正确,那么你想要的是什么可能被称为“re-firable 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;
}

未测试

所有方法都返回此选项以使其可链接。

如果我正确编写了构造函数,那么您可以通过将布尔值传递给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();

答案 3 :(得分:0)

看看这个解决方案。通过这种实现,您可以跟踪许多dinamic添加的承诺。如果您正在使用jQuery Deferred,您可以这样做。 jsFiddle

请注意,我已经使用了lodash库中的_.every方法,因此您还需要安装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甚至更短 在此解决方案中,我使用lodash库中的_.any方法

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