A talk by @thorstenfrommen
"How well is a task being executed?"
"Should I do anything?"
function foo() {
if ( ! check_condition() ) {
return /* data */;
}
// Perform actual task...
}
function foo( WP_Post $post ) {
if ( $post->post_status !== 'publish' ) {
return /* data */;
}
// Perform actual task...
}
function foo( WP_Post $post ) {
if ( $post->post_status !== 'publish' ) {
return /* data */;
}
if ( $post->post_type !== 'special' ) {
return /* data */;
}
// Perform actual task...
}
function foo( WP_Post $post ) {
if ( $post->post_type !== 'special' ) {
return /* data */;
}
if ( $post->post_status !== 'publish' ) {
return /* data */;
}
// Perform actual task...
}
function foo( WP_Post $post ) {
if ( $post->post_type !== 'special' ) {
return /* data */;
}
if ( ! expensive_check() ) {
return /* data */;
}
// Perform actual task...
}
"Is there anything to do?"
function process( int $post_id ) {
// Kick off some processes, maybe fire a WordPress action.
// Get $data from somewhere...
update_option( 'option_name', /* initial data */ );
foreach ( $data as $item ) {
// Process item...
update_option( 'option_name', /* updated data */ );
}
delete_option( 'option_name' );
}
function process( int $post_id ) {
// Get $data from somewhere...
if ( ! $data ) {
return;
}
// ...
}
"Let’s not spend any more time on this any time soon."
"Let’s keep this handy."
$foo = get_foo();
$foo = $data->foo_bar['foo'];
"Memo-what?"
wp.element.memo (React.memo)
React.useMemo()
_.memoize()
$data = get_data();
$filtered_data = array_filter( filter_data, $data );
$normalized_data = array_map( normalize_data, $filtered_data );
vs.
$data = get_data();
$data = array_filter( filter_data, $data );
$data = array_map( normalize_data, $data );
items.reduce( ( a, item ) => {
return [
...a,
Item,
];
}, [] );
vs.
items.reduce( ( a, item ) => {
a.push( item );
return a;
}, [] );
items.reduce( ( o, item ) => {
return {
...o,
[item.name]: item,
];
}, {} );
vs.
items.reduce( ( o, item ) => {
o[ item.name ] = item;
return o;
}, {} );
function action1( $post_or_id ) { /* ... */ }
function action2( $post_or_id ) { /* ... */ }
function action3( $post_or_id ) { /* ... */ }
/** @var int $post_id */
action1( $post_id );
action2( $post_id );
action3( $post_id );
vs.
$post = get_post( $post_id );
action1( $post );
action2( $post );
action3( $post );
get_post()
Queries
get_post( 42 );
↪ WP_Post::get_instance( 42 );
↪ $wpdb->query( "SELECT * FROM wp_posts WHERE ID = 42 LIMIT 1" );
No caching, by design!
"Let’s do this at a reasonable interval only."
// Execute callback for every single position change.
window.addEventListener( 'dragover', onDragOver );
vs.
// Execute callback every 200ms, at maximum.
throttledHandler = throttle( onDragOver, 200 );
window.addEventListener( 'dragover', throttledHandler );
"Let’s do this after some cool-down only."
// Perform search request for every single user input.
input.addEventListener( 'change', searchPosts );
vs.
// Perform search once the user stopped typing for 300ms.
debouncedSearch = debounce( searchPosts, 300 );
input.addEventListener( 'change', debouncedSearch );
(Memory Leaks)
"Let’s clean up leftover event handlers, potentially causing issues."
useEffect( () => {
window.addEventListener( 'dragover', onDragOver );
return () => {
window.removeEventListener( 'dragover', onDragOver );
};
}, [ onDragOver ] );
// Automatic self-removal after having been invoked.
button.addEventListener( 'click', doStuff, {
once: true,
} );
"Let’s clean up leftover timed callbacks, potentially causing issues."
useEffect( () => {
const id = setInterval( doSomething, 600 );
return () => {
clearInterval( id );
};
}, [ doSomething ] );
useEffect( () => {
const id = setTimeout( doSomething, 3000 );
return () => {
clearTimeout( id );
};
}, [ someValue ] );
import { withSafeTimeout } from '@wordpress/components';
class MyComponent extends React.Component {
scheduleAction() {
this.setTimeout( this.doAction, 5000 );
}
// No need to clear the timeout.
// ...
}
export default withSafeTimeout( MyComponent );
"Let’s prevent receiving/acting on data we don’t need anymore."
useEffect( () => {
const abortController = new AbortController();
apiFetch( {
path: '/some/path',
signal: abortController.signal,
} )
.then( /* ... */ )
.catch( () => {
if ( abortController.signal.aborted ) {
return;
}
// ...
} );
return () => {
abortController.abort();
};
}, [] );
"Let’s prevent executing tasks we don’t need anymore."
const debouncedFetchData = useMemo( () => {
debounce( fetchData, 300 );
}, [ fetchData ] );
const debouncedFetchData = useMemo( () => {
debounce( fetchData, 300 );
}, [ fetchData ] );
useEffect( () => {
return () => {
debouncedFetchData.cancel();
};
}, [ debouncedFetchData ] );
mysql> SET profiling = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS t1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> CREATE TABLE T1 (id INT);
Query OK, 0 rows affected (0.01 sec)
mysql> SHOW PROFILES;
+----------+----------+--------------------------+
| Query_ID | Duration | Query |
+----------+----------+--------------------------+
| 0 | 0.000088 | SET PROFILING = 1 |
| 1 | 0.000136 | DROP TABLE IF EXISTS t1 |
| 2 | 0.011947 | CREATE TABLE t1 (id INT) |
+----------+----------+--------------------------+
3 rows in set (0.00 sec)
(or Environment)
*_meta
tables for value-based querying.
(How was my performance? 😬)