src/lib/methods/fetch.js
function doFetch(store, key, url, options) {
  const { cacheControl, rejectOnError } = options;
  const value = get(store, key);
  const isMissing = !value;
  let isExpired = hasExpired(value && value[store.EXPIRY_KEY], false);

  store.debug('fetch %s from %s', key, url);

  // Load if missing or expired
  if (isMissing || isExpired) {
    if (!url) {
      return Promise.resolve({
        body: value,
        duration: 0,
        headers: {},
        key,
        status: 400
      });
    }

    return new Promise((resolve, reject) => {
      load(store, key, url, options)
        .then(res => {
          store.debug('fetched %s from %s', key, url);

          const body = get(store, key);

          resolve({
            body,
            duration: res.duration,
            headers: generateResponseHeaders(body[store.EXPIRY_KEY], cacheControl),
            key,
            status: res.status
          });
        })
        .catch(err => {
          // Abort if already destroyed
          if (store.destroyed) {
            return null;
          }

          isExpired = hasExpired(value && value[store.EXPIRY_KEY], true);

          if (rejectOnError && isExpired) {
            return reject(err);
          }

          store.debug('fetched stale %s after load error', key);
          return resolve({
            body: isExpired ? undefined : value,
            duration: 0,
            error: err,
            headers: generateResponseHeaders(value && value[store.EXPIRY_KEY], cacheControl, true),
            key,
            status: isExpired ? err.status : 200
          });
        });
    });
  }

  store.debug('fetched %s', key);
  return Promise.resolve({
    body: value,
    duration: 0,
    headers: generateResponseHeaders(value && value[store.EXPIRY_KEY], cacheControl),
    key,
    status: 200
  });
}
function doFetch(store, key, url, options) {
  const { cacheControl, rejectOnError } = options;
  const value = get(store, key);
  const isMissing = !value;
  let isExpired = hasExpired(value && value[store.EXPIRY_KEY], false);

  store.debug('fetch %s from %s', key, url);

  // Load if missing or expired
  if (isMissing || isExpired) {
    if (!url) {
      return Promise.resolve({
        body: value,
        duration: 0,
        headers: {},
        key,
        status: 400
      });
    }

    return new Promise((resolve, reject) => {
      load(store, key, url, options)
        .then(res => {
          store.debug('fetched %s from %s', key, url);

          const body = get(store, key);

          resolve({
            body,
            duration: res.duration,
            headers: generateResponseHeaders(body[store.EXPIRY_KEY], cacheControl),
            key,
            status: res.status
          });
        })
        .catch(err => {
          // Abort if already destroyed
          if (store.destroyed) {
            return null;
          }

          isExpired = hasExpired(value && value[store.EXPIRY_KEY], true);

          if (rejectOnError && isExpired) {
            return reject(err);
          }

          store.debug('fetched stale %s after load error', key);
          return resolve({
            body: isExpired ? undefined : value,
            duration: 0,
            error: err,
            headers: generateResponseHeaders(value && value[store.EXPIRY_KEY], cacheControl, true),
            key,
            status: isExpired ? err.status : 200
          });
        });
    });
  }

  store.debug('fetched %s', key);
  return Promise.resolve({
    body: value,
    duration: 0,
    headers: generateResponseHeaders(value && value[store.EXPIRY_KEY], cacheControl),
    key,
    status: 200
  });
}
Fetch data. If expired, load from 'url' and store at 'key'
@param {DataStore} store
@param {String} key
@param {String} url
@param {Object} options
- {Boolean} abort
- {Object} cacheControl
- {Boolean} ignoreQuery
- {Number} retry
- {Boolean} rejectOnError
- {Number} timeout
@returns {Promise}