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

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

问题描述

问题 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调用debounce,使得待发送的数据随着时间的推移积累起来,到了超时就批量发送.调用 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;
}

由于延迟被认为是一种反模式,(另见 什么时候有人需要创建延迟?),问题是 - 是否有可能在没有延迟的情况下实现相同的事情(或类似 new Promise(function (resolve, reject) {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

...通过将 then 调用与创建承诺的回调链接起来很容易构建,承诺表示将来创建它的可用性.

…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.

如果您要为承诺做出承诺,则永远不要使用延迟模式.当且仅当您想要等待一些异步操作时,您才应该使用 deferreds 或 Promise 构造函数,并且 它尚未涉及 Promise.在所有其他情况下,您应该组合多个承诺.

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

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

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) 如何从 callAPI2 中完全分解出去抖动逻辑(这可能不是必需的,但提出了一个很好的观点)以及 b) 承诺构造函数如何只关心自己超时,没有别的.它甚至不需要像延迟那样泄漏" 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.

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

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