我必须调用两个API来获取一些值。然后我能够将结果呈现给用户。我通过回调进行顺序调用实现了这一点:
function firstApi() {
callFirstApi(function(firstValue) {
secondApi(firstValue);
});
}
function secondApi(firstValue) {
callSecondApi(function(secondValue) {
presentResults(firstValue, secondValue);
});
}
function presentResults(firstValue, secondValue) {
// do something...
}
困扰我的问题是API调用可能是异步的。我想知道这种解决方案是否有任何问题:
var firstValue = null;
var secondValue = null;
function apiResult(value) {
if (firstValue === null) {
firstValue = value;
return;
}
secondValue = value;
presentResults();
}
function presentResults() {
// do something...
}
firstApiCall(apiResult);
secondApiCall(apiResult);
JavaScript是单线程的,但我仍然不确定上下文切换可能发生的位置。换句话说,如果在异步调用完成时有可能在执行过程中中断函数调用(因此,例如firstValue
null检查将为两个执行路径传递,第二个值将永远不会设定)。
答案 0 :(得分:6)
这实际上与线程无关(并且JavaScript不是单线程的,甚至不是在浏览器上,但除非你专门创建新线程,否则你只处理一个),只是异步性
如果我正确阅读,则表示您要拨打callFirstApi
,并致电callSecondApi
,当两项操作完成后,您都要拨打presentResults
。您不需要对API进行串行调用(一个接一个),如果它们并行(两个都在同一时间运行)就没问题,您只需要等待它们两个完成(无论如何)完成顺序)
您的解决方案不会(因为您怀疑)工作,因为结果的顺序可能与您要求的顺序不符,但该解决方案假设他们按照您调用的顺序执行完成callFirstApi
,callSecondApi
。您需要一个不假设第一个在第二个之前完成的假设的解决方案。
撰写异步操作的这个问题是开发 promises 的关键动机之一,它们在ES2015(又名ES6)及更高版本中是原生的,可用于polyfill库。当你需要做一些异步的事情串联(彼此另一个),你使用一个承诺链;当你需要同时做并时(它们可以重叠,你只想等到它们全部完成),你启动它们并使用Promise.all
等待它们全部到完整。
使用promises的示例如下所示:
Promise.all([callFirstApi(), callSecondApi()])
.then(function(results) {
// results[0] is the result from the first, results[1] is the result from the second
presentResults(results[0], results[1]);
})
.catch(function() {
// an error occurred in one of the calls
});
假设callFirstApi
和callSecondApi
返回承诺。如果他们不这样做,你可以保证 - 如果他们:
function promisify(f) {
return function() {
return new Promise(function(resolve, reject) {
f(function(result) {
if (/*...result is an error...*/) {
reject(/*...error here...*/);
} else {
resolve(/*...result here...*/);
}
});
}
}
(这很简单,但你明白了。)
然后:
// Once:
var firstApiWithPromises = promisify(callFirstApi);
var secondApiWithPromises = promisify(callSecondApi);
// As needed:
Promise.all([firstApiWithPromises(), secondApiWithPromises()])
.then(function(results) {
presentResults(results[0], results[1]);
})
.catch(function() {
// an error occurred in one of the calls
});
它们完成的顺序无关紧要,上面会在继续之前等待两者,并且会在results
数组中按顺序给出结果。