Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hooks: add support for async filters and actions #64204

Merged
merged 2 commits into from
Sep 27, 2024
Merged

Conversation

jsnajdr
Copy link
Member

@jsnajdr jsnajdr commented Aug 2, 2024

Adds support for async actions and filters, via new applyAsyncFilters and doAsyncAction methods.

You add an async function as a filter: its input is a synchronous value (the hook runner ensures that) and the output can be a promise:

addFilter( 'test.async.filter', 'callback_plus1', ( value ) => {
  return new Promise( ( r ) => setTimeout( () => r( value + 1 ), 10 ) );
} );
addFilter( 'test.async.filter', 'callback_times2', ( value ) => {
  return new Promise( ( r ) => setTimeout( () => r( value * 2 ), 10 ) );
} );

expect( await applyAsyncFilters( 'test.async.filter', 2 ) ).toBe( 6 );

With async actions the currentAction and currentFilter functions lose most of their meaning: they now return the hook that started running last and is still running. Although the other currently running hooks are also "current".

@youknowriad please try it out 🙂

@jsnajdr jsnajdr added the [Package] Hooks /packages/hooks label Aug 2, 2024
@jsnajdr jsnajdr requested a review from youknowriad August 2, 2024 13:21
@jsnajdr jsnajdr self-assigned this Aug 2, 2024
Copy link

github-actions bot commented Aug 2, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: jsnajdr <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: tyxla <[email protected]>
Co-authored-by: adamsilverstein <[email protected]>
Co-authored-by: Mamaduka <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link

github-actions bot commented Aug 2, 2024

Size Change: +107 B (+0.01%)

Total Size: 1.77 MB

Filename Size Change
build/hooks/index.min.js 1.65 kB +107 B (+6.93%) 🔍
ℹ️ View Unchanged
Filename Size
build-module/a11y/index.min.js 482 B
build-module/block-library/file/view.min.js 447 B
build-module/block-library/image/view.min.js 1.78 kB
build-module/block-library/navigation/view.min.js 1.16 kB
build-module/block-library/query/view.min.js 743 B
build-module/block-library/search/view.min.js 616 B
build-module/interactivity-router/index.min.js 3 kB
build-module/interactivity/debug.min.js 16.7 kB
build-module/interactivity/index.min.js 13.4 kB
build/a11y/index.min.js 952 B
build/annotations/index.min.js 2.26 kB
build/api-fetch/index.min.js 2.32 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 579 B
build/block-directory/index.min.js 7.26 kB
build/block-directory/style-rtl.css 1.07 kB
build/block-directory/style.css 1.07 kB
build/block-editor/content-rtl.css 4.42 kB
build/block-editor/content.css 4.41 kB
build/block-editor/default-editor-styles-rtl.css 394 B
build/block-editor/default-editor-styles.css 394 B
build/block-editor/index.min.js 256 kB
build/block-editor/style-rtl.css 15.6 kB
build/block-editor/style.css 15.5 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 149 B
build/block-library/blocks/audio/editor.css 151 B
build/block-library/blocks/audio/style-rtl.css 132 B
build/block-library/blocks/audio/style.css 132 B
build/block-library/blocks/audio/theme-rtl.css 134 B
build/block-library/blocks/audio/theme.css 134 B
build/block-library/blocks/avatar/editor-rtl.css 115 B
build/block-library/blocks/avatar/editor.css 115 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/button/editor-rtl.css 265 B
build/block-library/blocks/button/editor.css 265 B
build/block-library/blocks/button/style-rtl.css 538 B
build/block-library/blocks/button/style.css 538 B
build/block-library/blocks/buttons/editor-rtl.css 291 B
build/block-library/blocks/buttons/editor.css 291 B
build/block-library/blocks/buttons/style-rtl.css 328 B
build/block-library/blocks/buttons/style.css 328 B
build/block-library/blocks/calendar/style-rtl.css 240 B
build/block-library/blocks/calendar/style.css 240 B
build/block-library/blocks/categories/editor-rtl.css 132 B
build/block-library/blocks/categories/editor.css 131 B
build/block-library/blocks/categories/style-rtl.css 152 B
build/block-library/blocks/categories/style.css 152 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 122 B
build/block-library/blocks/code/theme.css 122 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 420 B
build/block-library/blocks/columns/style.css 420 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 124 B
build/block-library/blocks/comment-author-avatar/editor.css 124 B
build/block-library/blocks/comment-author-name/style-rtl.css 72 B
build/block-library/blocks/comment-author-name/style.css 72 B
build/block-library/blocks/comment-content/style-rtl.css 120 B
build/block-library/blocks/comment-content/style.css 120 B
build/block-library/blocks/comment-date/style-rtl.css 65 B
build/block-library/blocks/comment-date/style.css 65 B
build/block-library/blocks/comment-edit-link/style-rtl.css 70 B
build/block-library/blocks/comment-edit-link/style.css 70 B
build/block-library/blocks/comment-reply-link/style-rtl.css 71 B
build/block-library/blocks/comment-reply-link/style.css 71 B
build/block-library/blocks/comment-template/style-rtl.css 200 B
build/block-library/blocks/comment-template/style.css 199 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 228 B
build/block-library/blocks/comments-pagination/editor.css 217 B
build/block-library/blocks/comments-pagination/style-rtl.css 234 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 832 B
build/block-library/blocks/comments/editor.css 832 B
build/block-library/blocks/comments/style-rtl.css 632 B
build/block-library/blocks/comments/style.css 631 B
build/block-library/blocks/cover/editor-rtl.css 641 B
build/block-library/blocks/cover/editor.css 642 B
build/block-library/blocks/cover/style-rtl.css 1.62 kB
build/block-library/blocks/cover/style.css 1.6 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 86 B
build/block-library/blocks/details/style.css 86 B
build/block-library/blocks/embed/editor-rtl.css 331 B
build/block-library/blocks/embed/editor.css 331 B
build/block-library/blocks/embed/style-rtl.css 419 B
build/block-library/blocks/embed/style.css 419 B
build/block-library/blocks/embed/theme-rtl.css 133 B
build/block-library/blocks/embed/theme.css 133 B
build/block-library/blocks/file/editor-rtl.css 326 B
build/block-library/blocks/file/editor.css 326 B
build/block-library/blocks/file/style-rtl.css 278 B
build/block-library/blocks/file/style.css 279 B
build/block-library/blocks/file/view.min.js 324 B
build/block-library/blocks/footnotes/style-rtl.css 198 B
build/block-library/blocks/footnotes/style.css 197 B
build/block-library/blocks/form-input/editor-rtl.css 229 B
build/block-library/blocks/form-input/editor.css 229 B
build/block-library/blocks/form-input/style-rtl.css 357 B
build/block-library/blocks/form-input/style.css 357 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 344 B
build/block-library/blocks/form-submission-notification/editor.css 341 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 470 B
build/block-library/blocks/freeform/editor-rtl.css 2.6 kB
build/block-library/blocks/freeform/editor.css 2.6 kB
build/block-library/blocks/gallery/editor-rtl.css 946 B
build/block-library/blocks/gallery/editor.css 951 B
build/block-library/blocks/gallery/style-rtl.css 1.83 kB
build/block-library/blocks/gallery/style.css 1.82 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 334 B
build/block-library/blocks/group/editor.css 334 B
build/block-library/blocks/group/style-rtl.css 103 B
build/block-library/blocks/group/style.css 103 B
build/block-library/blocks/group/theme-rtl.css 79 B
build/block-library/blocks/group/theme.css 79 B
build/block-library/blocks/heading/style-rtl.css 188 B
build/block-library/blocks/heading/style.css 188 B
build/block-library/blocks/html/editor-rtl.css 346 B
build/block-library/blocks/html/editor.css 347 B
build/block-library/blocks/image/editor-rtl.css 785 B
build/block-library/blocks/image/editor.css 787 B
build/block-library/blocks/image/style-rtl.css 1.59 kB
build/block-library/blocks/image/style.css 1.59 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 1.65 kB
build/block-library/blocks/latest-comments/style-rtl.css 355 B
build/block-library/blocks/latest-comments/style.css 354 B
build/block-library/blocks/latest-posts/editor-rtl.css 179 B
build/block-library/blocks/latest-posts/editor.css 179 B
build/block-library/blocks/latest-posts/style-rtl.css 509 B
build/block-library/blocks/latest-posts/style.css 510 B
build/block-library/blocks/list/style-rtl.css 107 B
build/block-library/blocks/list/style.css 107 B
build/block-library/blocks/loginout/style-rtl.css 61 B
build/block-library/blocks/loginout/style.css 61 B
build/block-library/blocks/media-text/editor-rtl.css 321 B
build/block-library/blocks/media-text/editor.css 320 B
build/block-library/blocks/media-text/style-rtl.css 558 B
build/block-library/blocks/media-text/style.css 556 B
build/block-library/blocks/more/editor-rtl.css 427 B
build/block-library/blocks/more/editor.css 427 B
build/block-library/blocks/navigation-link/editor-rtl.css 644 B
build/block-library/blocks/navigation-link/editor.css 645 B
build/block-library/blocks/navigation-link/style-rtl.css 192 B
build/block-library/blocks/navigation-link/style.css 191 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 295 B
build/block-library/blocks/navigation-submenu/editor.css 294 B
build/block-library/blocks/navigation/editor-rtl.css 2.19 kB
build/block-library/blocks/navigation/editor.css 2.2 kB
build/block-library/blocks/navigation/style-rtl.css 2.25 kB
build/block-library/blocks/navigation/style.css 2.23 kB
build/block-library/blocks/navigation/view.min.js 1.03 kB
build/block-library/blocks/nextpage/editor-rtl.css 392 B
build/block-library/blocks/nextpage/editor.css 392 B
build/block-library/blocks/page-list/editor-rtl.css 378 B
build/block-library/blocks/page-list/editor.css 378 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 236 B
build/block-library/blocks/paragraph/editor.css 236 B
build/block-library/blocks/paragraph/style-rtl.css 341 B
build/block-library/blocks/paragraph/style.css 340 B
build/block-library/blocks/post-author-biography/style-rtl.css 74 B
build/block-library/blocks/post-author-biography/style.css 74 B
build/block-library/blocks/post-author-name/style-rtl.css 69 B
build/block-library/blocks/post-author-name/style.css 69 B
build/block-library/blocks/post-author/editor-rtl.css 107 B
build/block-library/blocks/post-author/editor.css 107 B
build/block-library/blocks/post-author/style-rtl.css 188 B
build/block-library/blocks/post-author/style.css 189 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 527 B
build/block-library/blocks/post-comments-form/style.css 528 B
build/block-library/blocks/post-content/editor-rtl.css 74 B
build/block-library/blocks/post-content/editor.css 74 B
build/block-library/blocks/post-content/style-rtl.css 79 B
build/block-library/blocks/post-content/style.css 79 B
build/block-library/blocks/post-date/style-rtl.css 62 B
build/block-library/blocks/post-date/style.css 62 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 155 B
build/block-library/blocks/post-excerpt/style.css 155 B
build/block-library/blocks/post-featured-image/editor-rtl.css 729 B
build/block-library/blocks/post-featured-image/editor.css 726 B
build/block-library/blocks/post-featured-image/style-rtl.css 347 B
build/block-library/blocks/post-featured-image/style.css 347 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 399 B
build/block-library/blocks/post-template/style.css 398 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 70 B
build/block-library/blocks/post-time-to-read/style.css 70 B
build/block-library/blocks/post-title/style-rtl.css 162 B
build/block-library/blocks/post-title/style.css 162 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 134 B
build/block-library/blocks/pullquote/editor.css 134 B
build/block-library/blocks/pullquote/style-rtl.css 342 B
build/block-library/blocks/pullquote/style.css 342 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 121 B
build/block-library/blocks/query-pagination-numbers/editor.css 118 B
build/block-library/blocks/query-pagination/editor-rtl.css 154 B
build/block-library/blocks/query-pagination/editor.css 154 B
build/block-library/blocks/query-pagination/style-rtl.css 237 B
build/block-library/blocks/query-pagination/style.css 237 B
build/block-library/blocks/query-title/style-rtl.css 64 B
build/block-library/blocks/query-title/style.css 64 B
build/block-library/blocks/query/editor-rtl.css 452 B
build/block-library/blocks/query/editor.css 451 B
build/block-library/blocks/query/view.min.js 958 B
build/block-library/blocks/quote/style-rtl.css 238 B
build/block-library/blocks/quote/style.css 238 B
build/block-library/blocks/quote/theme-rtl.css 233 B
build/block-library/blocks/quote/theme.css 236 B
build/block-library/blocks/read-more/style-rtl.css 138 B
build/block-library/blocks/read-more/style.css 138 B
build/block-library/blocks/rss/editor-rtl.css 101 B
build/block-library/blocks/rss/editor.css 101 B
build/block-library/blocks/rss/style-rtl.css 288 B
build/block-library/blocks/rss/style.css 287 B
build/block-library/blocks/search/editor-rtl.css 199 B
build/block-library/blocks/search/editor.css 199 B
build/block-library/blocks/search/style-rtl.css 672 B
build/block-library/blocks/search/style.css 671 B
build/block-library/blocks/search/theme-rtl.css 113 B
build/block-library/blocks/search/theme.css 113 B
build/block-library/blocks/search/view.min.js 475 B
build/block-library/blocks/separator/editor-rtl.css 100 B
build/block-library/blocks/separator/editor.css 100 B
build/block-library/blocks/separator/style-rtl.css 248 B
build/block-library/blocks/separator/style.css 248 B
build/block-library/blocks/separator/theme-rtl.css 195 B
build/block-library/blocks/separator/theme.css 195 B
build/block-library/blocks/shortcode/editor-rtl.css 286 B
build/block-library/blocks/shortcode/editor.css 286 B
build/block-library/blocks/site-logo/editor-rtl.css 806 B
build/block-library/blocks/site-logo/editor.css 803 B
build/block-library/blocks/site-logo/style-rtl.css 218 B
build/block-library/blocks/site-logo/style.css 218 B
build/block-library/blocks/site-tagline/editor-rtl.css 87 B
build/block-library/blocks/site-tagline/editor.css 87 B
build/block-library/blocks/site-tagline/style-rtl.css 65 B
build/block-library/blocks/site-tagline/style.css 65 B
build/block-library/blocks/site-title/editor-rtl.css 85 B
build/block-library/blocks/site-title/editor.css 85 B
build/block-library/blocks/site-title/style-rtl.css 143 B
build/block-library/blocks/site-title/style.css 143 B
build/block-library/blocks/social-link/editor-rtl.css 338 B
build/block-library/blocks/social-link/editor.css 338 B
build/block-library/blocks/social-links/editor-rtl.css 757 B
build/block-library/blocks/social-links/editor.css 756 B
build/block-library/blocks/social-links/style-rtl.css 1.51 kB
build/block-library/blocks/social-links/style.css 1.5 kB
build/block-library/blocks/spacer/editor-rtl.css 346 B
build/block-library/blocks/spacer/editor.css 346 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table-of-contents/style-rtl.css 83 B
build/block-library/blocks/table-of-contents/style.css 83 B
build/block-library/blocks/table/editor-rtl.css 394 B
build/block-library/blocks/table/editor.css 394 B
build/block-library/blocks/table/style-rtl.css 640 B
build/block-library/blocks/table/style.css 639 B
build/block-library/blocks/table/theme-rtl.css 152 B
build/block-library/blocks/table/theme.css 152 B
build/block-library/blocks/tag-cloud/editor-rtl.css 144 B
build/block-library/blocks/tag-cloud/editor.css 144 B
build/block-library/blocks/tag-cloud/style-rtl.css 266 B
build/block-library/blocks/tag-cloud/style.css 265 B
build/block-library/blocks/template-part/editor-rtl.css 368 B
build/block-library/blocks/template-part/editor.css 368 B
build/block-library/blocks/template-part/theme-rtl.css 113 B
build/block-library/blocks/template-part/theme.css 113 B
build/block-library/blocks/term-description/style-rtl.css 126 B
build/block-library/blocks/term-description/style.css 126 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 165 B
build/block-library/blocks/text-columns/style.css 165 B
build/block-library/blocks/verse/style-rtl.css 98 B
build/block-library/blocks/verse/style.css 98 B
build/block-library/blocks/video/editor-rtl.css 396 B
build/block-library/blocks/video/editor.css 397 B
build/block-library/blocks/video/style-rtl.css 192 B
build/block-library/blocks/video/style.css 192 B
build/block-library/blocks/video/theme-rtl.css 134 B
build/block-library/blocks/video/theme.css 134 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.1 kB
build/block-library/common.css 1.1 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 11.7 kB
build/block-library/editor.css 11.7 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 219 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.9 kB
build/block-library/style.css 14.9 kB
build/block-library/theme-rtl.css 708 B
build/block-library/theme.css 712 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 52.6 kB
build/commands/index.min.js 16.1 kB
build/commands/style-rtl.css 955 B
build/commands/style.css 952 B
build/components/index.min.js 226 kB
build/components/style-rtl.css 12.3 kB
build/components/style.css 12.3 kB
build/compose/index.min.js 12.7 kB
build/core-commands/index.min.js 3.11 kB
build/core-data/index.min.js 73.4 kB
build/customize-widgets/index.min.js 11 kB
build/customize-widgets/style-rtl.css 1.35 kB
build/customize-widgets/style.css 1.35 kB
build/data-controls/index.min.js 641 B
build/data/index.min.js 8.98 kB
build/date/index.min.js 18 kB
build/deprecated/index.min.js 458 B
build/dom-ready/index.min.js 325 B
build/dom/index.min.js 4.66 kB
build/edit-post/classic-rtl.css 578 B
build/edit-post/classic.css 580 B
build/edit-post/index.min.js 13.6 kB
build/edit-post/style-rtl.css 2.54 kB
build/edit-post/style.css 2.54 kB
build/edit-site/index.min.js 217 kB
build/edit-site/posts-rtl.css 7.36 kB
build/edit-site/posts.css 7.36 kB
build/edit-site/style-rtl.css 12.6 kB
build/edit-site/style.css 12.6 kB
build/edit-widgets/index.min.js 17.8 kB
build/edit-widgets/style-rtl.css 4.19 kB
build/edit-widgets/style.css 4.19 kB
build/editor/index.min.js 103 kB
build/editor/style-rtl.css 9.34 kB
build/editor/style.css 9.34 kB
build/element/index.min.js 4.83 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 8.11 kB
build/format-library/style-rtl.css 476 B
build/format-library/style.css 476 B
build/html-entities/index.min.js 445 B
build/i18n/index.min.js 3.58 kB
build/is-shallow-equal/index.min.js 526 B
build/keyboard-shortcuts/index.min.js 1.31 kB
build/keycodes/index.min.js 1.46 kB
build/list-reusable-blocks/index.min.js 2.18 kB
build/list-reusable-blocks/style-rtl.css 846 B
build/list-reusable-blocks/style.css 846 B
build/media-utils/index.min.js 3.2 kB
build/notices/index.min.js 946 B
build/nux/index.min.js 1.61 kB
build/nux/style-rtl.css 749 B
build/nux/style.css 745 B
build/patterns/index.min.js 7.34 kB
build/patterns/style-rtl.css 687 B
build/patterns/style.css 685 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 2.06 kB
build/preferences/index.min.js 2.9 kB
build/preferences/style-rtl.css 554 B
build/preferences/style.css 554 B
build/primitives/index.min.js 829 B
build/priority-queue/index.min.js 1.54 kB
build/private-apis/index.min.js 1.01 kB
build/react-i18n/index.min.js 630 B
build/react-refresh-entry/index.min.js 9.47 kB
build/react-refresh-runtime/index.min.js 6.76 kB
build/redux-routine/index.min.js 2.69 kB
build/reusable-blocks/index.min.js 2.55 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 10.1 kB
build/router/index.min.js 1.96 kB
build/server-side-render/index.min.js 1.94 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 2.04 kB
build/token-list/index.min.js 581 B
build/url/index.min.js 3.9 kB
build/vendors/react-dom.min.js 41.7 kB
build/vendors/react-jsx-runtime.min.js 560 B
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 965 B
build/warning/index.min.js 250 B
build/widgets/index.min.js 7.17 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.03 kB

compressed-size-action

@youknowriad youknowriad requested a review from gziolo August 2, 2024 14:49
@@ -943,3 +944,17 @@ test( 'checking hasFilter with named callbacks and removeAllActions', () => {
expect( hasFilter( 'test.filter', 'my_callback' ) ).toBe( false );
expect( hasFilter( 'test.filter', 'my_second_callback' ) ).toBe( false );
} );

test( 'async filter', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. :)

Can we have an example with actions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if you like it I'll finish the PR: add a full test suite and documentation 🙂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a few more tests. I'd also like to add more documentation, but the hooks package is not very well documented in the first place. It's like if it was assumed that everyone already knows how WordPress actions and filters work. There are *.md guides documenting specific actions and filters, but the API itself is not documented. Except some jsdoc comments which are however not propagated to any *.md docs.

@Mamaduka
Copy link
Member

Mamaduka commented Aug 5, 2024

With async actions the currentAction and currentFilter functions lose most of their meaning: they now return the hook that started running last and is still running.

Should we add this note in the currentAction and currentFilter doc blocks?

addAction( 'test.async.action', 'action2', action2 );

await doAsyncAction( 'test.async.action' );
expect( outputs ).toEqual( [ 1, 2, 3, 4 ] );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I wonder if this should be the right order. I feel like the correct order should be 1, 3, 2, 4 (in other words, internally we'd use Promise.all to allow for parallel actions).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the order doesn't matter in actions (as opposed to filters)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addAction also has priority as argument, both in JS and PHP versions. So the order matters and can be influenced.

The only difference between actions and filters is that filters process a "value" that is passed from one handler to another.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes true but I feel that for most actions, we don't really care about the order. I wonder if it should be an option. For instance, this has the waterfall effect if multiple plugins trigger REST APIs for instance in an async action...

Anyway, I can leave with both approaches.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I understand, the registered actions could also run in parallel, and there would be one Promise.all or Promise.allSettled that waits and only then doAsyncAction returns.

Why not, but that means entering a new territory where there is not precedent with existing PHP and JS actions. These all run sequentially.

Do you have a use case for async actions? Filters are easy, they always must be sequential, but actions can run in different modes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically, order should matter for both actions and filters, specifically if we want to mirror the hook behavior on the PHP side. Order does matter there, and is predictable. It's important for consistent behavior when you have multiple functions hooked to the same action.

Imagine you're hooking 10 functions from separate plugins, each of them registering tabs in a tabbed layout. If you care about the tab order (and you likely do), then you'll likely care about the consistency of the action order.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think about the "post save" action we have or the "pre save" action. How do you see plugins using this one?

The known use cases for preSavePost are:

Validations can run in parallel, because the result is a boolean success/fail. One failed validation is sufficient to fail them all.

Modifying edits must run sequentially, the output of one handler is fed to the next one.

For savePost (after the entity was saved to REST) the known use case is saving metaboxes. After the post itself is saved, the metabox forms are saved to _wpMetaBoxUrl. These handlers could run in parallel.

After some consideration, I believe that async actions are inherently parallel, because the input of any action doesn't depend on the output of any other. While async filters are inherently sequential.

I'm not sure about handling errors. Until now the sync hook didn't really support throwing errors. Stale data would be left inside hooksStore.__current if any handler threw an exception, there was no try/catch block.

In the current state of this PR, throwing an error immediately aborts the hook run. Further handlers are called only when all previous handlers succeed. It's not possible, for example, that a later handler recovers from an error thrown by an earlier one. Like you can do with promises: promise.then().catch().then(). I'm not sure if this lack of flexibility is a problem or not.

In PHP, a handler can return a wp_error and the next handler has complete freedom what to do with that error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Webpack has a very sophisticated system for various kinds of sync and async hooks, published separately as the tapable package. These are the kinds of async hooks that they provide:

  • AsyncSeriesHook: all handlers run one at at time, in a sequence. If any handler throws an error, further handlers are not called and the hook run finishes with that error. All handlers receive the same parameters (passed to the "run" function) and don't return anything. The return type of handlers and the "run" function is Promise<void>.
  • AsyncSeriesBailHook: a slight variation of the previous hook where a handler can abort the process not only by throwing an error, but also by returning a non-undefined value. If a handler has "something to say", it will return that, and it will become the result of the hook run. The same logic as with errors, but the semantic is not an error. The hook can have both a regular result and an error. The return type is Promise<T | undefined>.
  • AsyncSeriesWaterfallHook: here each handler's first argument is the return value of the previous handler. The remaining argument are constant. Very similar to a WordPress filter.
  • AsyncParallelHook: all handlers are running in parallel, started at the same time. If any of them throws an error, the hook run finishes with that error. The other handlers still run (we can't abort them) but their return values will be ignored. Very similar to Promise.all.
  • AsyncParallelBailHook: all handlers also run in parallel, and if any of them bails (returns non-undefined value) or throws, that becomes the result of the hook run. Other handlers continue to run but their results are ignored.

Currently, this PR implements an equivalent of AsyncSeriesHook (doAsyncAction) and AsyncSeriesWaterfallHook (applyAsyncFilters).

We could also add AsyncParallelHook, to do an action in parallel. Could be a parameter of doAsyncAction (parallel = true | false), but there is no good way to distinguish it from other parameters. So a better option is doAsyncActionParallel method.

The "bail" logic doesn't have any prior art in WordPress and I think we don't need it. It can be always simulated by each handler doing a check:

if ( value !== undefined ) {
  return value;
}
// start doing the actual job

There are WordPress filters, like determine_current_user, that are of the "bail" type and do exactly this.

So, what do you think? How much inspiration should we take here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still hesitant about which one do we need more parallel or not but I'm thinking that we should pick one for now and see how far we can get with it.

Your call, if you think "serial/waterfall" is better as default, we can go with it for now and consider a separate function later for parallel if need be.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the base webpack codebase, they have 42 async hook, and only 6 of them are parallel. The majority, 36, are serial. That suggests that parallel actions are an exception.

So, yes, I'll go with doActionAsync and applyFiltersAsync being serial by default. We can add doActionAsyncParallel at any time later when there is a demand.

Also, doActionAsync seems to be a better name than doAsyncAction. It follows a convention where the base name is doAction and there is a series of modifiers. Core has do_action_deprecated or do_action_ref_array, so this convention is already established. I'll modify the PR accordingly.

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @jsnajdr 👍

I think this could definitely use some more work on the documentation side. Folks coming from the backend world of WP might be confused as to why we have two different types of functions for executing the hooks. Maybe some examples could help with that.

@@ -2,6 +2,10 @@

## Unreleased

### Enhancements
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't these a new feature?

Suggested change
### Enhancements
### New Features

@@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- added new `doAsyncAction` and `applyAsyncFilters` functions to run hooks in async mode ([#64204](https://github.com/WordPress/gutenberg/pull/64204)).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- added new `doAsyncAction` and `applyAsyncFilters` functions to run hooks in async mode ([#64204](https://github.com/WordPress/gutenberg/pull/64204)).
- added new `doAsyncAction` and `applyAsyncFilters` functions to run hooks in async mode ([#64204](https://github.com/WordPress/gutenberg/pull/64204)).


/** @type {import('.').Store} filters */
this.filters = Object.create( null );
this.filters.__current = [];
this.filters.__current = new Set();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some concerns about switching the storage type, worried that someone could be using the internals (using the internals for the PHP version of the hooks is not uncommon), but I didn't spot any custom usage, so should be fine.

Copy link
Member

@adamsilverstein adamsilverstein Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious about why this is changed - is there a benefit to Set over [] or any risk this could break something? (eg is add not allowing duplicates a concern?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A hook run must be able to reliable add and remove itself to __current when it starts and ends. With async hooks, multiple hooks can run at the same time, independently and in parallel, and array .push and .pop is no longer reilable. You could be .popping not your own record, but someone else's. That gets fixed with a Set (of currently running hooks).

@@ -943,3 +945,86 @@ test( 'checking hasFilter with named callbacks and removeAllActions', () => {
expect( hasFilter( 'test.filter', 'my_callback' ) ).toBe( false );
expect( hasFilter( 'test.filter', 'my_second_callback' ) ).toBe( false );
} );

describe( 'async filter', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add tests that verify the functions that work with the current hook work well with async filters/actions? doingAction comes to mind.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests that verify that doingAction/doingFilter return correct results when multiple hooks are running at once, in parallel.

addAction( 'test.async.action', 'action2', action2 );

await doAsyncAction( 'test.async.action' );
expect( outputs ).toEqual( [ 1, 2, 3, 4 ] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically, order should matter for both actions and filters, specifically if we want to mirror the hook behavior on the PHP side. Order does matter there, and is predictable. It's important for consistent behavior when you have multiple functions hooked to the same action.

Imagine you're hooking 10 functions from separate plugins, each of them registering tabs in a tabbed layout. If you care about the tab order (and you likely do), then you'll likely care about the consistency of the action order.

@jsnajdr
Copy link
Member Author

jsnajdr commented Aug 8, 2024

Should we add this note in the currentAction and currentFilter doc blocks?

Maybe we could deprecate or outright remove them. A wpdirectory.net search showed that it's not used by any public plugin.

@jsnajdr
Copy link
Member Author

jsnajdr commented Sep 26, 2024

This PR is now ready for final review and merging, but there is one big problem: we missed the deadline for 6.7 enhancements, only bugfixes can be backported now.

It wouldn't be a big deal but this PR is a prerequisite for stabilizing the new editor.preSavePost and editor.savePost hooks, which currently have an __unstable prefix and therefore shouldn't be really added to Core. And a real stabilization with an API that we can guarantee for long term requires that they are async filters.

@youknowriad @noisysocks Is there still a chance to backport this enhancement before beta 1?

@jsnajdr
Copy link
Member Author

jsnajdr commented Sep 26, 2024

I think this could definitely use some more work on the documentation side.

It's true that the new hooks are not documented -- because the hooks package as a whole doesn't have any documentation at all. It's like there was an assumption at the beginning that everyone knows what hooks are, because it's a major WordPress PHP feature.

This can be fixed, of course, but probably in another PR that documents the package as a whole.

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to go from my perspective ✅

Thanks for the thorough tests!

🚀

@jsnajdr jsnajdr merged commit ebb58c8 into trunk Sep 27, 2024
64 checks passed
@jsnajdr jsnajdr deleted the add/async-hooks branch September 27, 2024 08:24
@github-actions github-actions bot added this to the Gutenberg 19.4 milestone Sep 27, 2024
@jsnajdr jsnajdr added the Backport to WP 6.7 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Sep 29, 2024
@github-actions github-actions bot removed the Backport to WP 6.7 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Sep 29, 2024
gutenbergplugin pushed a commit that referenced this pull request Sep 29, 2024
* Hooks: add support for async filters and actions

* Unit tests for doing/didAction/Filter
@github-actions github-actions bot added the Backported to WP Core Pull request that has been successfully merged into WP Core label Sep 29, 2024
Copy link

I just cherry-picked this PR to the wp/6.7 branch to get it included in the next release: f42a8d0

@fabiankaegy fabiankaegy added the Needs Dev Note Requires a developer note for a major WordPress release cycle label Oct 1, 2024
@fabiankaegy fabiankaegy mentioned this pull request Oct 1, 2024
97 tasks
@fabiankaegy
Copy link
Member

fabiankaegy commented Oct 2, 2024

Hey @jsnajdr 👋

Would you be able to help write a dev note for this for the 6.7 release? We are planning to have this as part of a larger Miscellaneous Editor Updates note.

We are hoping to get all drafts in by October 13th to leave some time for reviews before the RC1.

All Dev Notes get tracked in #65784 so feel free to leave a note there or ping me directly :)

Please let us know if you can assist with that.

Thanks in advance :)

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 3, 2024

Hi @fabiankaegy 👋 Yes, I'm going to write the dev note.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backported to WP Core Pull request that has been successfully merged into WP Core Needs Dev Note Requires a developer note for a major WordPress release cycle [Package] Hooks /packages/hooks [Type] Enhancement A suggestion for improvement.
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

6 participants