嵌套异步jquery承诺

时间:2017-02-10 19:39:37

标签: javascript jquery promise

我编写了一个小程序来帮助我理解如何在jquery中执行嵌套异步promise。

我想按顺序调用两个任务。第一个任务有两个也按顺序调用的子任务。这就是全部。

虽然这有效,但我怀疑这是否是使用承诺的最佳方式。必须为每个任务创建一个新的延迟感觉就像代码闻到我。例如,传递一个延迟对象并仅使用它会更好吗?谢谢!

https://plnkr.co/edit/dyFFqqZhCVuhBYCuWjzm?p=preview

doTasks().then(function(arg) {
    console.log(arg)
})

function doTasks() {
    var d = $.Deferred();
    task1().then(function(arg) {
        console.log(arg)
        task2().then(function(arg) {
            console.log(arg)
            d.resolve('all tasks are done')
        })
    })
    return d.promise();
}

function task1() {
    var d = $.Deferred();

    console.log("starting task1...")

    setTimeout(function() {
        task1A().then(function() {
            task1B().then(function() {
                d.resolve('task1 is done')
            })
        })
    }, 10);

    return d.promise();
}

function task1A() {
    var d = $.Deferred();
    console.log("starting task1A...")
    setTimeout(function() {
        console.log("  resolving task1A...")
        d.resolve();
    }, 1000);
    return d.promise();
}

function task1B() {
    var d = $.Deferred();
    console.log("starting task1B...")
    setTimeout(function() {
        console.log("  resolving task1B...")
        d.resolve();
    }, 1000);
    return d.promise();
}

function task2() {
    var d = $.Deferred();
    console.log("starting task2...")

    setTimeout(function() {
        d.resolve('task2 is done');
    }, 1000);
    return d.promise()
}

2 个答案:

答案 0 :(得分:2)

是的,创造许多不必要的延期是有点臭。

您无需为ajax操作创建任何延迟。如果从.then()处理程序中返回一个promise,它将自动链接到前一个promise(从而对它进行排序),并且所有jquery ajax调用都已返回promise。因此,您无需为ajax操作创建任何自己的承诺。并且,您可以链接而不是嵌套,以便对操作进行排序。

事实上,在您不需要时创建延期或承诺称为promise anti-pattern。您希望使用并返回已创建的承诺,而不是创建新承诺。并且,在可能的情况下,您希望将承诺链接起来而不是嵌套。

以下是如何在可运行的代码段中执行此操作而不创建一个延迟,除了在承诺中包装setTimeout()的一个地方。显然,如果你为它们创建了一个可重用的函数,你可以为你的所有任务使用更少的代码,但我假设这些只是真正的异步操作的占位符,所以我将它们保留原样(除了使用共享delay()功能)。

doTasks().then(function(arg) {
    console.log("everything done");
})

function doTasks() {
    return task1().then(function(arg) {
        console.log(arg);
        return task2().then(function(arg) {
            console.log(arg)
            return arg;
        })
    })
}

// make promise version of setTimeout() in one central place
// that can then be used elsewhere
function delay(t) {
    return $.Deferred(function(def) {
        setTimeout(function() {
            def.resolve();
        }, t);
    }).promise();
}

function task1() {
    console.log("starting task1...")
    return delay(10).then(function() {
        return task1A();
    }).then(function() {
        return task1B();
    });
}

function task1A() {
    console.log("starting task1A...")
    return delay(1000).then(function() {
        console.log("  resolving task1A...")
        return "done task1A";
    });
}

function task1B() {
    console.log("starting task1B...")
    return delay(1000).then(function() {
        console.log("  resolving task1B...")
        return "done task1B";
    });
}

function task2() {
    console.log("starting task2...")
    return delay(1000).then(function() {
        console.log("  task2 is done")
        return "done task2";
    });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

答案 1 :(得分:2)

你不应该承诺你的承诺。这个想法是你通过在第一个then回调中返回一个值来链接它们,并链接下一个then,它在自己的回调中将执行下一步,等等。

当您在多个地方使用计时器来解决承诺时,您可以创建一个函数,该函数将基于毫秒数返回此类承诺。

您甚至可以创建一个输出内容并再次返回承诺的函数。

这将允许你将整个链条连在一起:

&#13;
&#13;
doTasks().then(say.bind(null, 'all done'));

function say(msg) {
    console.log(msg);
    return $.Deferred().resolve(msg).promise();
}

function doTasks() {
    return task1().then(task2);
}

function delay(ms) { // one function for delaying as a promise
    var d = $.Deferred();
    setTimeout(d.resolve.bind(null, '  delay completed'), ms);
    return say('starting delay of ' + ms + ' milliseconds...')
        .then(d.promise).then(say);
}

function task1() {
    return say('starting task1...').then(delay.bind(null, 10))
        .then(task1A).then(task1B).then(say.bind(null, 'task1 done'));
}

function task1A() {
    return say('starting task1A...').then(delay.bind(null, 1000))
        .then(say.bind(null, '  resolving task1A...'));
}

function task1B() {
    return say('starting task1B...').then(delay.bind(null, 1000))
        .then(say.bind(null, '  resolving task1B...'));
}

function task2() {
    return say('starting task2...').then(delay.bind(null, 1000))
        .then(say.bind(null, 'task2 done'));
}
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
&#13;
&#13;
&#13;