Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
react-relay / lib / RelayTaskScheduler.js
Size: Mime:
'use strict';

var Promise = require('fbjs/lib/Promise');

/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule RelayTaskScheduler
 * @typechecks
 * 
 */

'use strict';

var invariant = require('fbjs/lib/invariant');

var queue = [];
var schedule;
var running = false;

/**
 * Task scheduler used by Relay internals. Each task is a synchronous unit of
 * work that can be deferred by an injected scheduler function. For example,
 * an injected scheduler can defer each task to the next animation frame:
 *
 *   RelayTaskScheduler.injectScheduler(function(executeTask) {
 *     // This function will be invoked whenever a task is enqueued. It will not
 *     // be invoked again until `executeTask` has been invoked. Also, invoking
 *     // `executeTask` more than once is an error.
 *     requestAnimationFrame(executeTask);
 *   });
 *
 * By default, the next task is executed synchronously after the previous one is
 * finished. An injected scheduler using `setImmediate` can alter this behavior.
 */
var RelayTaskScheduler = {
  /**
   * @public
   *
   * Injects a scheduling function that is invoked with a callback that will
   * execute the next unit of work. The callback will return a promise that
   * resolves with a new callback when the next unit of work is available.
   */
  injectScheduler: function injectScheduler(injectedScheduler) {
    schedule = injectedScheduler;
  },

  /**
   * @internal
   *
   * Enqueues one or more callbacks that each represent a synchronous unit of
   * work that can be scheduled to be executed at a later time.
   *
   * The return value of each callback will be passed in as an argument to the
   * next callback. If one of the callbacks throw an error, the execution will
   * be aborted and the returned promise be rejected with the thrown error.
   * Otherwise, the returned promise will be resolved with the return value of
   * the last callback. For example:
   *
   *   RelayTaskScheduler.enqueue(
   *     function() {
   *       return 'foo';
   *     },
   *     function(foo) {
   *       return 'bar';
   *     }
   *   ).then(
   *     function(bar) {
   *       // ...
   *     }
   *   );
   *
   *   RelayTaskScheduler.enqueue(
   *     function() {
   *       return 'foo';
   *     },
   *     function(foo) {
   *       throw new Error();
   *     },
   *     function() {
   *       // Never executed.
   *     }
   *   ).catch(
   *     function(error) {}
   *   );
    */
  enqueue: function enqueue() {
    for (var _len = arguments.length, callbacks = Array(_len), _key = 0; _key < _len; _key++) {
      callbacks[_key] = arguments[_key];
    }

    var promise = new Promise(function (resolve, reject) {
      var nextIndex = 0;
      var error = null;
      function enqueueNext(value) {
        if (error) {
          reject(error);
          return;
        }
        if (nextIndex >= callbacks.length) {
          resolve(value);
        } else {
          queue.push(function () {
            enqueueNext((function () {
              var nextCallback = callbacks[nextIndex++];
              try {
                value = nextCallback(value);
              } catch (e) {
                error = e;
                value = undefined;
              }
              return value;
            })());
          });
        }
      }
      enqueueNext(undefined);
    });
    scheduleIfNecessary();
    return promise;
  }
};

function scheduleIfNecessary() {
  if (running) {
    return;
  }
  if (queue.length) {
    running = true;
    var executeTask = createTaskExecutor(queue.shift());
    if (schedule) {
      schedule(executeTask);
    } else {
      executeTask();
    }
  } else {
    running = false;
  }
}

function createTaskExecutor(callback) {
  var invoked = false;
  return function () {
    !!invoked ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayTaskScheduler: Tasks can only be executed once.') : invariant(false) : undefined;
    invoked = true;
    invokeWithinScopedQueue(callback);
    running = false;
    scheduleIfNecessary();
  };
}

function invokeWithinScopedQueue(callback) {
  var originalQueue = queue;
  queue = [];
  try {
    callback();
  } finally {
    Array.prototype.unshift.apply(originalQueue, queue);
    queue = originalQueue;
  }
}

module.exports = RelayTaskScheduler;