问题1:在给定的时间只允许一个API请求,因此,真正的网络请求在尚未完成的情况下排队。应用可以随时调用API级别,并且期望得到回报。当API调用排入队列时,将在将来的某个时刻创建对网络请求的承诺- 返回什么给应用程序?这样可以通过延迟的“代理”承诺来解决:
var queue = []; function callAPI (params) { if (API_available) { API_available = false; return doRealNetRequest(params).then(function(data){ API_available = true; continueRequests(); return data; }); } else { var deferred = Promise.defer(); function makeRequest() { API_available = false; doRealNetRequest(params).then(function(data) { deferred.resolve(data); API_available = true; continueRequests(); }, deferred.reject); } queue.push(makeRequest); return deferred.promise; } } function continueRequests() { if (queue.length) { var makeRequest = queue.shift(); makeRequest(); } }
问题2:对一些API调用进行反跳动,以便随着时间的推移累积要发送的数据,然后在达到超时时分批发送。调用API的应用期望得到回报。
var queue = null; var timeout = 0; function callAPI2(data) { if (!queue) { queue = {data: [], deferred: Promise.defer()}; } queue.data.push(data); clearTimeout(timeout); timeout = setTimeout(processData, 10); return queue.deferred.promise; } function processData() { callAPI(queue.data).then(queue.deferred.resolve, queue.deferred.reject); queue = null; }
对尚未建立的承诺的承诺
…很容易通过将then调用与创建承诺的回调链接起来来构建,以保证将来可以创建承诺。
then
如果您承诺一个承诺,则 永远不要 使用延迟模式。Promise当且仅当您要等待异步且 尚未涉及promise 时,才应使用deferreds或构造函数。在所有其他情况下,您应该撰写多个承诺。
Promise
当你说
当API调用排入队列时,将在将来的某个时刻创建对网络请求的承诺
那么您不应该创建一个延后的协议,以便在创建承诺后就可以稍后解决(或者更糟的是,一旦实现承诺,就可以用承诺结果来解决它),而应该在将来的某个时刻获得承诺网络要求将完成。基本上你要写
return waitForEndOfQueue().then(makeNetworkRequest);
当然,我们将需要分别更改队列。
var queue_ready = Promise.resolve(true); function callAPI(params) { var result = queue_ready.then(function(API_available) { return doRealNetRequest(params); }); queue_ready = result.then(function() { return true; }); return result; }
这具有额外的好处,您将需要显式处理队列中的错误。在这里,一旦一个请求失败(您可能想要更改它),每个调用都会返回一个被拒绝的承诺- 在您的原始代码中,queue刚被卡住(您可能没有注意到)。
queue
第二种情况稍微复杂一些,因为它确实涉及到setTimeout调用。这是一个异步原语,我们需要手动为其构建承诺- 但仅用于超时,而没有其他目的。再次,我们将获得超时的承诺,然后只需将我们的API调用链接到该请求即可获得我们想要返回的承诺。
setTimeout
function TimeoutQueue(timeout) { var data = [], timer = 0; this.promise = new Promise(resolve => { this.renew = () => { clearTimeout(timer); timer = setTimeout(resolve, timeout); }; }).then(() => { this.constructor(timeout); // re-initialise return data; }); this.add = (datum) => { data.push(datum); this.renew(); return this.promise; }; } var queue = new TimeoutQueue(10); function callAPI2(data) { return queue.add(data).then(callAPI); }
您可以在此处看到:a)如何完全消除抖动的逻辑callAPI2(可能没有必要,但有一点很重要),以及b)promise构造函数如何仅与超时有关,而没有其他问题。它甚至不需要resolve像延迟的那样“泄漏”该功能,它唯一可用于外部的就是renew允许扩展计时器的功能。
callAPI2
resolve
renew