链接2个异步调用(promise API)以串行运行
Chaining 2 asynchronous calls (promise API) to run serially
这与我今天发布的问题类似,但需要将请求串联起来。我有两个异步请求,其中第二个请求需要第一个请求的结果来发送查询。
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 3")
永远不会在成功的场景中打印,因为我在console.log("At 2")
之后返回。但是当我运行时,在控制台中我看到这些命令
console.log("At 1")
console.log("At 3")
console.log("At 2")
我认为then
会阻塞,直到它从承诺得到响应(由get()返回)。someFn中的所有东西都是串行执行的。这个假设错了吗?将两个使用promise的异步操作串联起来串行运行的最佳方法是什么?
谢谢。
编辑:
我尝试了Ketan建议的在AngularJs中链接Ajax调用。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
铬端子在undefined
之后显示At 2
。我不知道为什么someFn
返回的结果没有分配给res
。
您遇到困难的地方是.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
返回一个promise,并且someFn()
也将始终返回一个promise。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
不阻塞
我想它会阻塞,直到它得到承诺的响应
。JS中的承诺不是透明的、阻塞的未来,而只是一个链回调的模式。promise在回调执行之前返回,At 3
在.then
返回之后记录,但在回调执行之前。如果在回调中使用了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")
});
- 与运行长作业(javascript,node.js)的第三方API同步的最佳实践
- 全屏API无法在Chrome/iOS上运行
- Codecademy代码在他们的网站上连接到Youtube API,但赢得了't在本地运行
- 正在运行时检测Facebook API版本
- 在Javascript中的for循环中运行不同的api
- 当运行Google“;Hello Analytics"API教程
- 在不同端口上运行时,rest api应用程序服务器(express)和Angulars js应用程序之间的Cors问题
- 页面可见性 API 在窗口最小化时无法在 Chrome OS X 上运行
- 确定 Google 地图 API 在运行时是否使用了无效密钥
- Javascript API在主站点上运行良好,但在外部服务器上无法正常工作
- 谷歌地理编码仅在没有API密钥的情况下运行
- 当 rest api 应用程序服务器(express)和 Angulars js 应用程序在不同的端口上运行时,Cors
- Youtube Javascript API:当用户切换“静音”时运行函数
- jQuery - 窗口可见性 API 以保持运行
- Facebook API,使用Facebook登录,不仅在IE上运行
- Upstart说我的脚本正在运行,但API在我重新启动进程之前不返回数据
- “尝试在清除的范围内运行编译和运行脚本” - 使用 Google Graph API 在 Drupal 中出错
- 谷歌地图API JavaScript不在html文件中运行,但在w3school中运行
- 在独立JavaScript程序上运行API
- 如何使用Rest API在需要身份验证的jenkins上运行作业