从javascript中的回调中获取两个值

时间:2016-12-31 18:22:12

标签: javascript

我必须调用两个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检查将为两个执行路径传递,第二个值将永远不会设定)。

1 个答案:

答案 0 :(得分:6)

这实际上与线程无关(并且JavaScript不是单线程的,甚至不是在浏览器上,但除非你专门创建新线程,否则你只处理一个),只是异步性

如果我正确阅读,则表示您要拨打callFirstApi,并致电callSecondApi,当两项操作完成后,您都要拨打presentResults。您不需要对API进行串行调用(一个接一个),如果它们并行(两个都在同一时间运行)就没问题,您只需要等待它们两个完成(无论如何)完成顺序)

您的解决方案不会(因为您怀疑)工作,因为结果的顺序可能与您要求的顺序不符,但该解决方案假设他们按照您调用的顺序执行完成callFirstApicallSecondApi。您需要一个不假设第一个在第二个之前完成的假设的解决方案。

撰写异步操作的这个问题是开发 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
    });

假设callFirstApicallSecondApi返回承诺。如果他们不这样做,你可以保证 - 如果他们:

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数组中按顺序给出结果。