Skip to content

Commit

Permalink
[FEATURE] Adds Cache API
Browse files Browse the repository at this point in the history
Adds the cache API specified in RFC 615. Since this API is essentially
just exposing the API that was implemented in Glimmer VM, and tested there,
I don't think tests are necessary to add in Ember itself.
  • Loading branch information
Chris Garrett committed May 19, 2020
1 parent 7618403 commit d392665
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/@ember/-internals/metal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export { Mixin, aliasMethod, mixin, observer, applyMixin } from './lib/mixin';
export { default as inject, DEBUG_INJECTION_FUNCTIONS } from './lib/injected_property';
export { tagForProperty, tagForObject, markObjectAsDirty, CUSTOM_TAG_FOR } from './lib/tags';
export { tracked } from './lib/tracked';
export { createCache, getValue, isConst } from './lib/cache';

export {
NAMESPACES,
Expand Down
128 changes: 128 additions & 0 deletions packages/@ember/-internals/metal/lib/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { createCache as glimmerCreateCache, getValue, isConst } from '@glimmer/validator';

/**
Ember uses caching based on trackable values to avoid updating large portions
of the application. This caching is exposed via a cache primitive that can be
used to cache a specific computation, so that it will not update and will
return the cached value until a tracked value used in its computation has
updated.
@module @glimmer/tracking/primitives/cache
@public
*/

/**
Receives a function, and returns a wrapped version of it that memoizes based on
_autotracking_. The function will only rerun whenever any tracked values used
within it have changed. Otherwise, it will return the previous value.
```js
import { tracked } from '@glimmer/tracking';
import { createCache, getValue } from '@glimmer/tracking/primitives/cache';
class State {
@tracked value;
}
let state = new State();
let computeCount = 0;
let counter = createCache(() => {
// consume the state. Now, `counter` will
// only rerun if `state.value` changes.
state.value;
return ++computeCount;
});
getValue(counter); // 1
// returns the same value because no tracked state has changed
getValue(counter); // 1
state.value = 'foo';
// reruns because a tracked value used in the function has changed,
// incermenting the counter
getValue(counter); // 2
```
@method createCache
@static
@for @glimmer/tracking/primitives/cache
@public
*/
export function createCache<T>(fn: () => T) {
return glimmerCreateCache(fn);
}

/**
Gets the value of a cache created with `createCache`.
```js
import { tracked } from '@glimmer/tracking';
import { createCache, getValue } from '@glimmer/tracking/primitives/cache';
let computeCount = 0;
let counter = createCache(() => {
return ++computeCount;
});
getValue(counter); // 1
```
@method getValue
@static
@for @glimmer/tracking/primitives/cache
@public
*/

/**
Can be used to check if a memoized function is _constant_. If no tracked state
was used while running a memoized function, it will never rerun, because nothing
can invalidate its result. `isConst` can be used to determine if a memoized
function is constant or not, in order to optimize code surrounding that
function.
```js
import { tracked } from '@glimmer/tracking';
import { createCache, getValue, isConst } from '@glimmer/tracking/primitives/cache';
class State {
@tracked value;
}
let state = new State();
let computeCount = 0;
let counter = createCache(() => {
// consume the state
state.value;
return computeCount++;
});
let constCounter = createCache(() => {
return computeCount++;
});
getValue(counter);
getValue(constCounter);
isConst(counter); // false
isConst(constCounter); // true
```
If called on a cache that hasn't been accessed yet, it will throw an
error. This is because there's no way to know if the function will be constant
or not yet, and so this helps prevent missing an optimization opportunity on
accident.
@method isConst
@static
@for @glimmer/tracking/primitives/cache
@public
*/

export { getValue, isConst };
2 changes: 2 additions & 0 deletions packages/@ember/canary-features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const DEFAULT_FEATURES = {
EMBER_GLIMMER_SET_COMPONENT_TEMPLATE: true,
EMBER_ROUTING_MODEL_ARG: true,
EMBER_GLIMMER_IN_ELEMENT: true,
EMBER_CACHE_API: null,
};

/**
Expand Down Expand Up @@ -81,3 +82,4 @@ export const EMBER_GLIMMER_SET_COMPONENT_TEMPLATE = featureValue(
);
export const EMBER_ROUTING_MODEL_ARG = featureValue(FEATURES.EMBER_ROUTING_MODEL_ARG);
export const EMBER_GLIMMER_IN_ELEMENT = featureValue(FEATURES.EMBER_GLIMMER_IN_ELEMENT);
export const EMBER_CACHE_API = featureValue(FEATURES.EMBER_CACHE_API);
13 changes: 12 additions & 1 deletion packages/ember/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { Registry, Container } from '@ember/-internals/container';
import * as instrumentation from '@ember/instrumentation';
import { meta } from '@ember/-internals/meta';
import * as metal from '@ember/-internals/metal';
import { FEATURES, isEnabled, EMBER_GLIMMER_SET_COMPONENT_TEMPLATE } from '@ember/canary-features';
import {
FEATURES,
isEnabled,
EMBER_GLIMMER_SET_COMPONENT_TEMPLATE,
EMBER_CACHE_API,
} from '@ember/canary-features';
import * as EmberDebug from '@ember/debug';
import { assert, captureRenderTree, deprecate } from '@ember/debug';
import Backburner from 'backburner';
Expand Down Expand Up @@ -329,6 +334,12 @@ Ember.observer = metal.observer;
Ember.mixin = metal.mixin;
Ember.Mixin = metal.Mixin;

if (EMBER_CACHE_API) {
Ember._createCache = metal.createCache;
Ember._cacheGetValue = metal.getValue;
Ember._cacheIsConst = metal.isConst;
}

/**
A function may be assigned to `Ember.onerror` to be called when Ember
internals encounter an error. This is useful for specialized error handling
Expand Down
3 changes: 3 additions & 0 deletions tests/docs/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -596,5 +596,8 @@ module.exports = {
'wrapModelType',
'wrapRecord',
'yield',
'createCache',
'getValue',
'isConst',
],
};

0 comments on commit d392665

Please sign in to comment.