react-relay
Version:
A framework for building data-driven React applications.
166 lines (155 loc) • 4.55 kB
JavaScript
/**
* 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
*
*/
;
var Promise = require('fbjs/lib/Promise');
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.await(
* function() {
* return 'foo';
* },
* function(foo) {
* return 'bar';
* }
* ).then(
* function(bar) {
* // ...
* }
* );
*
* RelayTaskScheduler.await(
* function() {
* return 'foo';
* },
* function(foo) {
* throw new Error();
* },
* function() {
* // Never executed.
* }
* ).catch(
* function(error) {}
* );
*/
await: function await() {
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;