【问题标题】:Sequential function calls in javascriptjavascript中的顺序函数调用
【发布时间】:2014-06-20 11:55:39
【问题描述】:

我希望函数 A 完成执行,并且只有在函数 B 开始执行之后。当我调用函数 A 然后调用函数 B 时,似乎两者都在同时执行。在函数 B 完成后,我想调用第三个函数 update_dropdown()。

我的代码如下所示:

function A {
    for (var i = 0; i < 5; i++) {
        var promise = $.get(url+i);
        $.when(promise).then(function () {
            $.post(url);
        });
    }
}
function B {
    var x = $.get(url);
    var promise = $.post(url+x);
    $.when(promise0).then(function () {
        update_dropdown();
    });
}

请您告诉我如何让这 3 个函数调用按顺序进行。

【问题讨论】:

  • 不完全确定你的目标是什么,你能再解释一下吗?
  • 假设:1.你调用函数A,想等待$.get完成,然后你想调用函数B,最后一旦B完成你想调用update_dropdown()?跨度>
  • 如果你想要一个准确的答案,你真的必须详细说明你想要的调用顺序是什么。您的代码没有多大意义(而且它是伪代码),因此不清楚您真正希望事情发生的顺序。例如,在A() 中,您想要五个$.get()并行操作,然后当它们全部完成时,$.post() 操作?在B(),你真的想做$.get(),然后不等待任何事情,做$.post()吗?
  • 是的@Pepto 你的假设是正确的,“你调用函数 A,想要等待 $.get 完成,然后你想要调用函数 B,最后一旦 B 完成你想要调用 update_dropdown ()"
  • 在函数 A 中,我想要 5 个连续的 get&post 请求。先获取后发帖。这必须发生5次。然后函数-B,必须执行。在 B 中有一个 $get,我从中获得一些价值,然后发布该价值。 (抱歉这部分没有显示在我的代码中,造成了一些混乱。)然后调用函数 update_dropdown()。

标签: javascript jquery object promise deferred


【解决方案1】:

我们可以使用 jquery Deferred Object 以我们的方式调用我们的选择函数。

很简单,让我们看看成功运行的例子:

<body>
<script
    src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">

// I want to call function in order of  f1,f2,f3,f4 every time when i will execute this html page.

    promise = f1().then(f2).then(f3).then(f4); // Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.

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

        setTimeout(function() {
            // our code here....
            alert("1");
            console.log("1");
            d.resolve(); // resolve() :Resolve a Deferred object and call any doneCallbacks with the given args.
        },1000); // You set some time for each method.
        return d.promise(); //promise(): Return a Deferred’s Promise object.

    }
    function f2() {
        var d = $.Deferred();

        setTimeout(function() {
            alert("2");
            console.log("2");
            d.resolve();
        },1000);
        return d.promise();
    }
    function f4() {
        var d = $.Deferred();

        setTimeout(function() {
            alert("4");
            console.log("4");
            d.resolve();
        },1000);
        return d.promise();
    }
    function f3() {
        var d = $.Deferred();

        setTimeout(function() {
            alert("3");
            console.log("3");
            d.resolve();
        },1000);
        return d.promise();
    }


</script>

【讨论】:

  • 呃,pipe 多年来已被弃用?
  • 哦,谢谢,我明白了,在 jquery 1.8 中不推荐使用 pipe()。所以我只是用 deferred.then() 的另一个强大的方法 then() 进行了编辑。
【解决方案2】:

假设假设 ...

让我们假设:

  • 每个get的url与其对应的post的url相同
  • 每个 get-post 对的 url 应该不同
  • A 中的五个 get-post 对可以并行发生,我们对返回的数据不感兴趣

首先,一个效用函数:

function getThenPost(url, appendToURL) {
    return $.get(url).then(function(x) {
        return (appendToURL) ? $.post(url + x) : $.post(url);
    });
}

然后是 A 和 B,两者都调用该实用程序:

function A(urls) {
    return $.when.apply(null, urls.map(function(url) {
        return getThenPost(url, false);
    }));
}
function B(url) {
    return getThenPost(url, true);
}

最后是一个调用 A 和 B 的表达式:

A(['/path/0', '/path/1', '/path/2', '/path/3', '/path/4']).then(function() {
    B('/path/5');
}).then(update_dropdown);

如果假设 1 和 2 不正确,调整此代码应该相当简单。

如果假设 3 不正确,则 A 将需要更广泛的修改。

【讨论】:

  • 对函数A的更正。
【解决方案3】:

这样的东西应该可以工作

function A {
    var xhr = [];

    for (var i = 0; i < 5; i++) {
        xhr.push( $.get(url) );
    }

    $.when.apply($, xhr).then(B);
}

function B {

    $.get(url).done(function(x) {
        $.post(url + x).done(update_dropdown);
    });

}

注意使用数组来保存承诺,然后使用$.whenapply() 在循环中的所有ajax 请求完成时触发回调。

【讨论】:

  • 您在函数A()B() 中没有与OP 相同的功能,并且缺少两个操作。
  • @jfriend00 - 在函数 A 中,$.when 调用在 OP 代码的循环内,为每个请求调用 B 函数,我认为这是问题所在是将其移出循环。在B 函数中我错过了对$.get(url) 的调用,否则它是相同的,但是OP 在A 中调用$.get(url) 五次,所以我认为调用该函数不是很恰当再次在B 中,如果是,则等待两个调用完成的过程将与A 中的相同
  • 是的,我想它并不完全清楚 OP 真正想要的调用序列。仅供参考,.when() 可能无关紧要,但是您可以使用 $.when.apply($, xhr); 更准确地模拟 $.when(...),在其中传递 $ 而不是 undefined 所以 this 设置为 $
  • 另外,$.when( $.post(url) ).then(update_dropdown); 可以是:$.post(url).then(update_dropdown);
  • 从每个函数中return 可能是个好主意,这样您就可以挂钩所有已完成的工作并编写它。
【解决方案4】:

好的,尽管仍有至少两个选项可供选择,但您真正想要的内容正在变得更加清晰(根据您最近的 cmets 来解决澄清问题)。

对于这样的操作,您可能希望利用许多 Promise 功能:

  1. jQuery 的 Ajax 调用已经返回了一个 Promise,因此您可以直接使用它们
  2. 要序列化操作,您可以将多个 Promise 操作链接在一起
  3. 要使异步操作正确序列化,您可以从.then() 处理程序返回一个promise,并且只有在所有链接的promise 都已解决时,主promise 才会解决(一种内置的$.when(),而无需显式调用$.when())。
  4. 您可以根据需要将任意数量的操作链接在一起,当它们全部完成时,主 Promise 会告诉您。
  5. 如果您从 A()B() 返回 Promise,那么这些函数的调用者可以监视它们何时使用 Promise 方法完成,然后您可以链接 A().then(B) 以对这两者进行排序。
  6. 当您使用链接对操作进行排序时,先前的方法解析数据将作为.then() 处理函数的第一个参数传递给链中的下一个.then() 处理函数,因此如果您需要下一个操作的先前数据,就可以使用。

因此,有了所有这些功能,只需在代码周围放置正确的脚手架即可实现您想要的精确排序。这里有两种不同的选择:


选项 1: 如果您想序列化 A() 中的所有内容,以便所有 10 个请求都以串行方式发生(下一个请求仅在前一个请求完成后进行),那么它可能看起来像这样:

// serialize all requests
function A() {
    var p = $.get(url).then(function(data) {return $.post(url)});
    for (var i = 1; i < 5; i++) {
        // chain four more pairs of requests onto the original promise
        p = p.then(function() {return $.get(url)})
             .then(function(data) {return $.post(url)});
    }
    // return the promise so callers can monitor when A() is done
    return p;
}


function B() {
    // sequence these three operations one after the other
    return ($.get(url)
       .then(function(data) {return $.post(url + x)})
       .then(update_dropdown)
    );
}

// run them both, one after the other
A().then(B);

选项 2: 如果您希望 A() 中的 5 对请求并行运行,只有 A() 的最后一部分等待 5 对请求完成,然后它可能看起来像这样:

// parallelize pairs of requests
function A() {
    var promises = [];
    for (var i = 0; i < 5; i++) {
        // execute 5 pairs of requests where each pair is serialized in itself
        promises.push($.get(url).then(function(data) {return $.post(url)}));
    }
    // return a promise that resolves only when all the other promises are done
    return $.when.apply($, promises);
}

function B() {
    // sequence these three operations one after the other
    return ($.get(url)
       .then(function(data) {return $.post(url + x)})
       .then(update_dropdown)
    );
}

// run them both, one after the other
A().then(B);

这些使用的概念是,如果您从 .then() 处理函数返回一个 Promise,那么它将把多个异步操作链接在一起,并且只有当所有链接的操作都被解决时,主 Promise 才会被解决。这对于对多个 ajax 操作进行排序非常强大,您甚至可以像您一样在循环中执行操作。

【讨论】:

  • 我尝试了选项 2。我的请求未按预期顺序生成。来自功能 A 和 B 的请求也变得混杂。它不是来自函数 A 的所有请求,然后是来自函数 B 的所有请求。这是我在网络请求日志中看到的。
  • @PriyankaA - 选项 2 是并行的。从您早期的 cmets 中,您需要选项 1,以便对 A() 中的内容进行序列化。另外,你能告诉我们你的实际代码吗?我们不能真正调试伪代码。如果来自函数 A()B() 的请求混合在一起,则说明没有正确实现,因为选项 1 或选项 2 都不会这样做。
  • @PriyankaA - 我不知道您打算发布什么,但那是关于无法读取的 100x100 图像。如果您要发布代码,请不要截图。使用编辑按钮并将其添加到您的问题中。如果没有看到您的实际代码,我真的无法帮助您调试。
  • 是的,我完全同意你的看法。在任何情况下,功能 A 和 B 的请求都不应混合,可能是选项 1 或 2。这是我的 code 的链接
  • @PriyankaA - 我看到的第一个问题是unattachSTARs() 应该以return $.when.apply($,promises); 结尾,而不是return $.when.apply($.promises);。注意逗号而不是句点。
【解决方案5】:

无需额外工作的 Javascript 是单线程的。这意味着功能不能同时执行。但问题是 $.get() 和 $.post() 调用是异步的。这意味着只要请求的数据到达您的客户端,它们就会执行。 (先到先得)

一种解决方案是在所有结果 ob A 到达后执行函数 B,或者保留所有结果并立即处理所有数据然后运行 ​​update_dropdown()

【讨论】:

  • 我认为他们理解这一点,只是在问如何做到这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多