对未使用延迟[反]模式而尚未创建的承诺的承诺 [英] Promises for promises that are yet to be created without using the deferred [anti]pattern

查看:108
本文介绍了对未使用延迟[反]模式而尚未创建的承诺的承诺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题1:在给定时间只允许一个API请求,因此真实的网络请求排队,而尚未完成的请求。应用程序可以随时调用API级别并期望获得回报。当API调用排队时,将在未来的某个时刻创建网络请求的承诺 - 返回应用程序的内容是什么?这就是如何通过延迟的代理承诺来解决:

Problem 1: only one API request is allowed at a given time, so the real network requests are queued while there's one that has not been completed yet. An app can call the API level anytime and expecting a promise in return. When the API call is queued, the promise for the network request would be created at some point in the future - what to return to the app? That's how it can be solved with a deferred "proxy" promise:

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的应用程序期待作为回报的承诺。

Problem 2: some API calls are debounced so that the data to be sent is accumulated over time and then is sent in a batch when a timeout is reached. The app calling the API is expecting a promise in return.

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;
}

由于延迟被视为反模式,(另请参阅什么时候有人需要创建延期?),问题是 - 是否有可能在没有延迟的情况下实现相同的事情(或等同的黑客,如新的承诺(函数(解决,拒绝){outerVar = [resolve,reject]}); ),使用标准的Promise API?

Since deferred is considered an anti-pattern, (see also When would someone need to create a deferred?), the question is - is it possible to achieve the same things without a deferred (or equivalent hacks like new Promise(function (resolve, reject) {outerVar = [resolve, reject]});), using the standard Promise API?

推荐答案


对尚未创建的承诺的承诺

Promises for promises that are yet to be created

...通过链接然后调用很容易构建,回调创建对promise的承诺代表可用性在将来创建它。

…are easy to build by chaining a then invocation with the callback that creates the promise to a promise represents the availability to create it in the future.

如果您承诺承诺,您应该永远使用延迟模式。当且仅当存在您想要等待的异步时,您应该使用延迟或 Promise 构造函数,并且它还没有涉及promises 。在所有其他情况下,你应该撰写多个承诺。

If you are making a promise for a promise, you should never use the deferred pattern. You should use deferreds or the Promise constructor if and only if there is something asynchronous that you want to wait for, and it does not already involve promises. In all other cases, you should compose multiple promises.

当你说


当API调用排队时,将在以后的某个时间点创建网络请求的承诺

When the API call is queued, the promise for the network request would be created at some point in the future

那么你不应该创建一个延迟,你可以在以后用承诺解决它(或者更糟糕的是,一旦承诺结算后用承诺结果解决它),但是你应该得到未来网络要求的承诺做成。基本上你要写

then you should not create a deferred that you can later resolve with the promise once it is created (or worse, resolve it with the promises results once the promise settles), but rather you should get a promise for the point in the future at which the network reqest will be made. Basically you're going to write

return waitForEndOfQueue().then(makeNetworkRequest);

当然我们需要分别改变队列。

and of course we're going to need to mutate the queue respectively.

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;
}

这有额外的好处,你需要明确处理错误队列。在这里,一旦一个请求失败(你可能想要改变它),每个调用都会返回一个被拒绝的promise - 在原始代码中,队列刚被卡住(你可能没有注意到。)

This has the additional benefit that you will need to explicitly deal with errors in the queue. Here, every call returns a rejected promise once one request failed (you'll probably want to change that) - in your original code, the queue just got stuck (and you probably didn't notice).

第二种情况有点复杂,因为它确实涉及 setTimeout 调用。这是一个异步原语,我们需要手动建立一个承诺 - 但仅限于超时,而不是其他任何东西。再一次,我们将获得超时的承诺,然后简单地将我们的API调用链接到那个以获得我们想要返回的承诺。

The second case is a bit more complicated, as it does involve a setTimeout call. This is an asynchronous primitive that we need to manually build a promise for - but only for the timeout, and nothing else. Again, we're going to get a promise for the timeout, and then simply chain our API call to that to get the promise that we want to return.

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)如何将去抖动逻辑完全分解出来code> callAPI2 (这可能不是必要但是提出了一个很好的观点)和b)promise构造函数如何只关注超时而不是其他任何东西。它甚至不需要像延迟那样泄漏 resolve 函数,它唯一可用于外部的是 renew 允许扩展计时器的函数。

You can see here a) how the debouncing logic is completely factored out of callAPI2 (which might not have been necessary but makes a nice point) and b) how the promise constructor only concerns itself with the timeout and nothing else. It doesn't even need to "leak" the resolve function like a deferred would, the only thing it makes available to the outside is that renew function which allows extending the timer.

这篇关于对未使用延迟[反]模式而尚未创建的承诺的承诺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆