【问题标题】:Chaining 2 asynchronous calls (promise API) to run serially链接 2 个异步调用(promise API)以串行运行
【发布时间】:2013-05-01 02:15:53
【问题描述】:

这类似于我发布的问题today,但需要串行链接请求。我有两个异步请求,第二个请求需要第一个请求的结果来发送查询。

var Db.get = function(key){
    var deferred = $q.defer();

     //send async req
    var req = ....
    req.success = function(d){
        deferred.resolve(d)
    };
    req.failure = function(d){
        deferred.reject(d)
    }

    return deferred.promise;
}

var someFn = function(id){
    Db.get(id, "abc")
        .then(function (d) {
            console.log("At 1")
            Db.get(d.id, "def")
                .then(function (d) {
                    console.log("At 2")
                    return d
                }, function (e) {
                    //error
                });
        }, function (e) {
            //error
        });

    console.log("At 3")
};

我应该想错了,因为我希望在console.log("At 2") 之后返回时,console.log("At 3") 永远不会在成功的情况下被打印出来。但是当我运行时,在控制台中我看到了这些顺序

console.log("At 1")
console.log("At 3")
console.log("At 2")

我在想then 会阻塞直到它得到承诺的响应(由 get() 返回)。因此, someFn 中的所有内容都是串行执行的。这个假设是错误的吗?链接两个异步操作(使用 Promise)以串行运行的最佳方式是什么。

谢谢。

编辑:

我尝试了 Ketan 建议的 Chaining Ajax calls in AngularJs

var someFn = function(id){
            Db.get(id, "abc")
                .then(function (d) {
                    console.log("At 1")
                    return Db.get(d.id, "def")
                }).then(function (d) {
                    console.log("At 2")
                    return d
                }, function (e) {
                    //error
                    return null;
                }).then(function (d) {
                    return d;
        });

        console.log("At 3")
    };

不过,如果我像这样打电话

var res = someFn(1)
console.log(res) /// undefined

chrome 终端在undefined 之后显示At 2。我不确定为什么someFn 重新调整的结果没有分配给res

【问题讨论】:

标签: javascript angularjs promise


【解决方案1】:

您遇到困难的地方是.then实际上并没有阻止。它可以帮助您将同步代码转换为异步代码,但它不适合您。让我们从考虑您尝试重写的同步代码开始。想象一下Db.get 是一个返回值而不是承诺的同步函数:

var someFn = function (id){
    try {
        var d = Db.get(id, "abc");
        console.log("At 1");
        var d = Db.get(d.id, "def");
        console.log("At 2")
        return d;
    } catch (ex) {
        console.log("At 3")
    }
};

在这种情况下,当我调用 someFn 时,我得到一个值,而不是一个承诺。也就是说整个函数是同步的。

如果我们暂时快进几年并想象我们可以使用 ES6。这将使我们将您的函数重写为:

var someFn = $q.async(function* (id){
    try {
        var d = yield Db.get(id, "abc");
        console.log("At 1");
        var d = yield Db.get(d.id, "def");
        console.log("At 2")
        return d;
    } catch (ex) {
        console.log("At 3")
    }
});

看起来很相似,但是这次我们让Db.get 返回了一个承诺,而someFn() 也将始终返回一个承诺。 yield 关键字实际上“暂停”当前函数,直到实现承诺。这让它看起来就像同步代码,但实际上是异步的。

回到现在,我们需要弄清楚如何写这个。 .then 调用的第二个参数是错误处理程序,因此与 ES6 示例完全等价的是:

var someFn = function (id){
    return Db.get(id, "abc")
        .then(function (d) {
            console.log("At 1");
            return Db.get(d.id, "def");
        })
        .then(function (d) {
            console.log("At 2");
            return d;
        })
        .then(null, function (ex) {
            console.log("At 3")
        });
});

注意每个返回如何仅从其当前函数范围返回。没有办法强制它一直跳出someFn

另一个有趣的实验是:

Db.get('id', 'abc')
  .then(function () {
    console.log('B');
  });
console.log('A');

以上将始终记录:

A
B

因为.then 不会阻止。

【讨论】:

    【解决方案2】:

    我在想然后会阻止直到它得到承诺的响应

    没有。 JS 中的 Promise 不是透明的、阻塞的 future,而只是一种链接回调的模式。在执行回调之前返回承诺 - 并且在 .then 返回之后但在执行回调之前记录At 3。而且如果你在回调中return,那么外部的someFn 就没有关系了。

    你宁愿使用类似的东西

    var someFn = function(id){
        return Db.get(id, "abc")
          .then(function (d) {
            console.log("At 1")
            return Db.get(d.id, "def");
          })
          .then(function (d) {
            console.log("At 2")
            return d
          }, function (e) {
            //error
          });
    }
    someFn().then(function(d) {
        console.log("At 3")
    });
    

    【讨论】:

    • 同理,如果 someFn 在链中排在第 5 位会怎样。因此,promise 在链中的某个地方强制链中调用的所有函数与 promise 一起工作。类似于在链中抛出异常。简而言之,如果 someFn 被另一个函数(someFn2)调用,那么 someFn2 的调用者是否也需要使用 .then() 来调用 someFn2。
    猜你喜欢
    • 1970-01-01
    • 2013-04-24
    • 2016-07-12
    • 2015-12-22
    • 1970-01-01
    • 2020-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多