RFC: First class support for promises and async/await

Adds first class support for reading the result of a JavaScript Promise using Suspense:

Introduces support for async/await in Server Components. Write Server Components using standard JavaScript await syntax by defining your component as an async function.
Introduces the use Hook. Like await, use unwraps the value of a promise, but it can be used inside normal components and Hooks, including on the client.

This enables React developers to access arbitrary asynchronous data sources with Suspense via a stable API.
View the rendered text

What would be the naming convention for functions only with use?
const customFunctionThatCanBeCalledConditionallyButOnlyInRender = (p, c) => {
  return [use(p), use(c)];
};

It can't be useFoo as eslint rule check will be false positive. So, no naming convention?

What would be the naming convention for functions only with use?

You're supposed to use a Hook. It will work in the runtime but the linter will forbid it. It's the same restriction as other Hooks: use is only permitted inside components and other custom Hooks.
It's an intentional decision that this proposal would effectively prevent arbitrary function calls from being allowed to suspend. There are a few reasons for this, one of them being that it allows an auto-memoizing compiler to more effectively reuse computations.
But arguably the primary reason is the concern you just raised: it makes it easier to tell which functions are allowed to call it.

Ah, I missed that part. So, we can't hide use within another function. It's a very unfortunate limitation from the runtime solution perspective, but maybe required for the compiler.

Brian Vaughn joined Replay ( https://replay.io ) a few months ago, and we've been intentionally using pre-alpha/experimental Suspense behavior for data fetching.  The pattern Brian introduced is a "Suspense cache", which primarily uses a Wakeable type (basically a sync promise) and then throws it to trigger Suspense behavior:

replay-devtools: /suspense caches folder
Reusable createGenericCache implementation

We also have helpers that allow us to fetch data in a typical async function, such as from a Redux thunk.
Typical usage looks like:
// Suspense in a component
function ColumnBreakpoints(props) {
  // actually fetch and suspend

  const breakpoints = getBreakpointPositionsSuspense(replayClient, focusedSourceId, visibleLines);

  // rendering
}

// In an async function such as a thunk
export function selectNode(nodeId: string, reason?: SelectionReason): UIThunkAction {
  return async (dispatch, getState, { ThreadFront, replayClient, protocolClient }) => {

    const nodes = await getNodeDataAsync(
      protocolClient,
      replayClient,
      ThreadFront.sessionId!,
      ThreadFront.currentPause!.pauseId!,
      { type: "parentNodes", nodeId }
    );

    // more logic
  }
}

Again, to be clear, we're very knowingly using pre-pre-alpha / experimental behavior here :)  and we've come up with a couple custom abstractions like this createGenericCache API for our own use.
Given that, a few questions / concerns from reading this:

The React team has previously talked about this "Promise cache" pattern along the lines of SomeDataCache.read(id), where that cache would itself throw the promise if the data doesn't exist yet and it starts a request.  I know this RFC talks about a future cache API, but doesn't go into details yet.  If use is the replacement for the existing-but-now-deprecated "throw a promise" behavior, is the SomeDataCache.read(id) pattern no longer a goal or planned usage approach?  Or is it that the "Promise cache" would exist and just return the promise instead of throwing it?
We've definitely got plenty of cases in our codebase where logic outside of React needs to trigger data fetching, which may then tie into Suspense rendering.  Any direction on how that would be done here?
The restriction on "no use calls in other functions" feels very restrictive at first glance, although I don't have concrete counter-examples off the top of my head

The React team has previously talked about this "Promise cache" pattern along the lines of SomeDataCache.read(id), where that cache would itself throw the promise if the data doesn't exist yet and it starts a request. I know this RFC talks about a future cache API, but doesn't go into details yet. If use is the replacement for the existing-but-now-deprecated "throw a promise" behavior, is the SomeDataCache.read(id) pattern no longer a goal or planned usage approach?

cache will be the built-in cache API — it's an evolution of the experimental <Cache /> API that is currently in the experimental channel. It has different implementation details but if you're currently using <Cache /> then cache should fit your needs just as well.

Or is it that the "Promise cache" would exist and just return the promise instead of throwing it?

Yeah you would return the promise, then the caller would unwrap it with use. It might feel a bit strange at first if you're used to the old Suspense pattern, but it's not that different from how you would write code in the async/await paradigm.

We've definitely got plenty of cases in our codebase where logic outside of React needs to trigger data fetching, which may then tie into Suspense rendering. Any direction on how that would be done here?

Yeah the cache RFC will cover this, too. You'll be able to call it from outside React and then reuse the cached response during rendering.

Ah, I missed that part. So, we can't hide use within another function. It's a very unfortunate limitation from the runtime solution perspective, but maybe required for the compiler.

It would also just be really hard to keep track of which functions are only called from React, without a solid naming convention. We could introduce a new naming convention that's different from hooks but it doesn't seem worth adding yet-another special type of function for only this case.
In practice I don't think it will feel that limiting, just as it's usually not a big deal that custom Hooks can't be conditional.

Some reactions over in the Reddit /r/reactjs thread:
https://www.reddit.com/r/reactjs/comments/y30uga/react_rfc_first_class_support_for_promises_and/

For proper support of async / await in the client I would assume first-class support of AbortSignals:
// if a function is passed instead of a promise, it will be called with a signal.
// upon "unrendering" of the component the signal is aborted.
const note = use(({ signal }) => fetchNote(id, { signal }));

Also, with the proposed API I am wondering how to distinguish an empty result form a finished result:
const note = use(Promise.resolve(undefined))

maybe a different API would prevent future head-aches and workarounds?
const { state, result, error } = use(Promise.resolve(undefined))
if (state === 0) // waiting
if (state === 1) // successful
if (state === -1) // errored

With suspend status will never be "waiting". Just success/error. Error can be thrown. Can we have ErrorBoundary in hooks API?
react-query does a great job with promises.

With suspend status will never be "waiting". Just success/error. Error can be thrown. Can we have ErrorBoundary in hooks API?

The status for that is "pending" — if you read a promise that's still pending, it will suspend and trigger the nearest Suspense fallback.
If the status is "error" it will trigger the nearest error boundary.

react-query does a great job with promises.

Libraries like React Query can essentially work the same as they do today. They can either use use internally, or they can return a promise to the caller, and then the caller would unwrap the result of the promise with use.

Libraries like React Query can essentially work the same as they do today. They can either use use internally, or they can return a promise to the caller, and then the caller would unwrap the result of the promise with use.

const promise = useQuery(...);
const data = use(promise);

feels like a very clunky user-facing API, so I don't think it will catch on.
Just to get this right though: The ability to call use conditionally will be lost, once exposed as part of a custom hook? A useQuery hook for example, built on top of use, won't be callable conditionally, right?
I understand use and cache very much as primitives that regular users shouldn't or shouldn't have to interact with. Most of the time they will be an implementation detail of some 3rd-party solution or data fetching abstraction.
I feel like use's ability to be called conditionally would be used as an escape hatch to the established hooks behavior: "Would be great if I could just do this fetch here conditionally, but our established useData hook won't allow me to" "Ah right, use can be called conditionally, maybe I can just use it for this one special case" Time passes,. System gets out of sync. Yada yada yada
If use's ability to be called conditionally is lost in 90% of real world use cases, why even divert from established patterns? What are your thoughts?

@tobias-tengler https://github.com/reactjs/rfcs/pull/229#issuecomment-1277948794

const promise = useQuery(...);
const data = use(promise);
feels like a very clunky user-facing API, so I don't think it will catch on.

In practice we expect it would look more like this:
const data = use(fetchQuery(...));

which you could also call from a non-React function like this:
const data = await fetchQuery(...);

@acdlite I know this is really starting to get over into the territory of the cache API RFC that doesn't exist yet, but what would that sort of "adapted library API" look like?
I'm specifically thinking of the major existing data fetching libs here like Apollo, SWR, React Query, and RTK Query.  All of them have settled on a fairly similar useQuery()-style API that returns something like { data, isFetching }.
How would you propose a library expose that same general functionality in a non-hook form?

@markerikson https://github.com/reactjs/rfcs/pull/229#issuecomment-1278030280

All of them have settled on a fairly similar useQuery()-style API that returns something like { data, isFetching }.

use isn't going to be useful for those APIs anyway, because it requires Suspense. Libraries or APIs that aren't interested in integrating with Suspense can keep doing what they're already doing with their Hook-based APIs.
But presumably some of those libraries have landed on that convention because Suspense for data fetching hasn't been stable until now, and once it is stable (with use) they'll switch to Suspense.

Also note that it's totally possible to do this:
const data = use(useQuery(...));

maybe with like an opt in flag to return a promise instead of suspending directly. Then you don't have to adopt the cache API, you can just do whatever you're already doing.
It looks clunkier compared to the version that suspends internally but the nice thing is that the user has the option to conditionally suspend based on some other value.

use isn't going to be useful for those APIs anyway, because it requires Suspense. Libraries or APIs that aren't interested in integrating with Suspense can keep doing what they're already doing with their Hook-based APIs.

That's my point and question, in two ways:

There's a ton of code in the ecosystem that is using these hooks.  Is the expectation that libraries would provide an alternate API, and that the intended path is that ecosystem would stop using useQuery() hooks?  That's asking an awful lot and implies a ton of migration.
Give that these hooks already have a defined result of "some object like {data, isFetching}, returning a Promise from the query hook would be a complete breaking change.  I do think a {suspense: true}type option might be more feasible, but even there that would start to wreak havoc with TS types.  I could more imagine auseQueryhook _calling_use()` internally.

But this goes back to what I was asking a minute ago.
Given that these hooks exist and have broad adoption, how would you envision an alternate "fetch this data" API for the same library looking like?

There's a ton of code in the ecosystem that is using these hooks. Is the expectation that libraries would provide an alternate API, and that the intended path is that ecosystem would stop using useQuery() hooks? That's asking an awful lot and implies a ton of migration.

Well yeah but there's nothing forcing them to migrate immediately. It'll be incremental, like when we introduced Hooks — we didn't delete class components from the universe, but it turned out that enough people liked the new pattern that most of the community eventually switched over.
Ultimately it comes down to whether the community at large finds Suspense compelling enough. If they don't, they can stay with the existing APIs. If they do, then here's a new set of functionality that is unlocked by that.

Given that these hooks exist and have broad adoption, how would you envision an alternate "fetch this data" API for the same library looking like?

Yeah I would probably add a separate API. Similar to when Redux introduced useSelector. That's also the strategy Relay used when they started migrating to Suspense a few years ago.
The two APIs can share internal implementation but the public interface remains clean (e.g. types, as you pointed out).

Suggestion: not only allow
use(promise)

but also
use({
  promise
})

or
use({
  [useSymbol]: promise
})

that way, useQuery could just add that promise or [promiseSymbol] property and React could latch on that.
That way, use(useQuery()) would be possible without a complete api change.

So useQuery could just add that promise or [promiseSymbol] property and React could latch on that.

It actually works with any thenable object. So if you add a then method that implements the Promise interface, that'll work.

However, I'm not really sure what that solves because if the data is already part of that object then what's the point of suspending with use?

@acdlite the data might not already be part - it could still be in a pending status at that point - the current implementation does not suspend (we have read the changelog and know that's experimental so we did not include that in the official api)

@acdlite the data might not already be part - it could still be in a pending status at that point - the current implementation does not suspend (we have read the changelog and know that's experimental so we did not include that in the official api).

Yeah sorry I edited my comment right after sending it when I realized that :D

I also edited the second half into my answer ^^
Any thoughts on that? If possible, we'd love to expose that promise to React, but not to the user.

It's something to consider, at least. My inclination would be to make it more like [useSymbol] and then you could call other usable things in there, like context. But probably should be a separate RFC.

@acdlite https://github.com/reactjs/rfcs/pull/229#issuecomment-1277808487

In practice I don't think it will feel that limiting, just as it's usually not a big deal that custom Hooks can't be conditional.

I think it's very limiting if we further explore use only solutions, because we can't extract reusable logic into functions. Off the top of my head, I'd have some promises in a context, and use the context and use one of promises. And that will be my library code.
We will be having more reusable patterns when we have more use(usable) patterns in the future.
Maybe it will be mitigated with the cache RFC.
(I have another real use case in practice, but it's probably a rare case.)

Well yeah but there's nothing forcing them to migrate immediately. It'll be incremental, like when we introduced Hooks — we didn't delete class components from the universe, but it turned out that enough people liked the new pattern that most of the community eventually switched over.
Ultimately it comes down to whether the community at large finds Suspense compelling enough. If they don't, they can stay with the existing APIs. If they do, then here's a new set of functionality that is unlocked by that.

This is comparison is a bit confusing. The way I am interpreting it is that the new use API enables Suspense and a new set of functionality that is unlocked by it… however Suspense is already supported by APIs like Relay’s hooks and they already achieves the functionality of Suspense.
My initial interpretation of the RFC is that use is an attempt to bridge the gap between the land of asynchronous JavaScript and React for use cases that can’t rely on data fetching libraries like Relay. Not that this is a replacement for Suspense-powered hooks, but this comment makes it sound like it is meant as a replacement for those hooks (of course a gradual one)

@dai-shi You can call async functions and use their Promises. It's the same principle for why async functions are annotated and have await.

@dai-shi You can call async functions and use their Promises. It's the same principle for why async functions are annotated and have await.

I might still be missing something, but can we use use(context) in those async functions?

Yea, no that's a limitation. They have to be Hooks or you have to read it on the outside and pass it in. If Hooks could be made conditional and used in some loops, then it becomes less limiting though.

Is there a way we could also support async iterables? From what I understand, you couldn't use them with use because they're not necessarily generator functions, and you couldn't use them from server components because they're not async functions.

I’m curious, it feels like this spec is going through a couple hoops to support this syntax on the client:
use(fetchData(id));

Why not be more strict and say “for good performance promises need to be referentially equal across renders”. Which would change the example to:
use(useMemo(() => fetchData(id), [id]));

(I guess an auto-memoizing compiler would do this.)
Carefully writing async functions so that they resolve in a microtask for the same inputs feels tricky to maintain over time as people naturally add more awaits. (What if a developer adds an await trackEvent() without thinking about how the function is called?)
This isn’t addressed in the spec but should these async functions also need to be idempotent if they’re called in React render? That’s much harder to maintain when the network is involved.
Requiring solutions at the callsite like cache() and use(useMemo()) seems easier to maintain than solutions deep in the implementation like fetchFromCache()?

Excited to see this! Is there room to make the syntax a bit more searchable? I imagine aspiring developers trying to google things like why isnt use working inside a function and having a bad time. Is there a reason it couldn't be called useUnwrap or something else that isn't one of the top 100 words in the English language?

Keep in mind that this is intended to be a very primitive feature even more versatile than await as it'll support other types. It's not one of many. For comparison, Svelte uses a single letter $ to denote a similar concept. I don't think I'd want fromReactiveValue(...) in its place.
It can also only be used inside other Hooks so it builds on the preexisting knowledge. With custom syntax it could also extend to use CustomHook() to be even more versatile.
If it's versatile enough it's just something you learn.

The suspended execution grosses me out. Hooks are already too much magic and this takes it even further and uses exceptions for control flow 🤢.
From a usability perspective it'd be better for the component to be able to respond to the state changes as promises don't have just one state but 3: pending, resolved, and rejected. Most of the time when I'm writing UI I actually want to show different things based on each of these states. If requests are pending, show a spinner. If request failed show error message and if request succeeds I want to do the right thing with the data.
I'm using TS most of the time so I do this with tagged unions but the same thing works in vanilla js:
type FuturePending = {kind: 'FuturePending'}
type FutureRejected = {kind: 'FutureRejected', message: string}
type FutureResolved<T> = {kind: 'FutureResolved', payload: T}

type Future<T> = FuturePending | FutureRejected | FutureResolved<T>

const useFuture = <T>(promise: Promise<T>): Future<T> => {
...
}

// helper for ergonomics
const matchFuture = <T>(future: Future<T>, {pending, resolved, rejected}) => {
  switch(future.kind){
    case 'FuturePending': return pending;
    case 'FutureRejected': return rejected(future.message);
    case 'FutureResolved': return resolved(future.payload);
  }
}

const Page = () => {
  return matchFuture(useFuture(fetchUser()), {
    pending: <Loading />,
    rejected: (message) => <Error message={message} />
    resolved: (user) => <h1>Hi! {user.username}</h1>
  });
}

As I hope you'll see, this gives better flexibility and doesn't involve any magic.

IMHO, for async/await scenarios use() sounds too generic, maybe something like useAwait() would be better.
If use() is intended for much more than that, maybe it's alright (but I'm not so sure).

@calebmer

Why not be more strict and say “for good performance promises need to be referentially equal across renders”.

This is something I was questioning too.
If the argument to use is the promise itself, it seems like it would then be making those calls (like network calls) on every single render.
Seems like having the argument instead be a function that returns a promise would enable more performance.
At that point it becomes more like a useAsyncMemo kind of structure, just with the added support for suspense.

My general understanding of async-ness and React is that we were pushing it outside React as much as possible. Frameworks like Next and Remix are showing how data-fetching should actually be outside of React all-together in order to avoid waterfall effects and be able to fetch and then render as much as possible.
With React Server Components this seemed like the general progression where server components would contain all that ugly async logic that we wanted to push away from our client components, and our client components would remain these pure props -> UI functions.
In other words, my intuition for the future was:

Data fetching only ever happens in server components.
Need to fetch something on the client? Wrap it in a server component.
Ideally a single server component could be deployed on the edge/cloud on the fly, so even tiny interactions that require browser fetch()es could now be in a server component.

What is the use case for use() and defining async-fetching-logic on the client instead of the server?

Interesting RFC! I'm questioning the naming of the hook, but can't think of anything better just yet. The issue is that regular hooks have established rules that everyone has by now accepted, and use not only doesn't have some of those rules, it has its own new rules as well, for e.g. it can't be called outside a React component (Iu understand technically one can, but it would be seen as compiler error), or at least shouldn't be inside non-react closure, which means you can't create custom hooks out of it(?).
I feel this definitely will lead to a lot of cognitive load. Not only a developer has to unlearn the useState+useEffect way of dealing with async data, at the same time also learn that now they can run the code conditionally just for this specific usecase.

Would it be better to disassociate it with hooks rather than ensure it's going to be one of its kind?

@bogas04 where did you get the idea that it can be only called in function components and not inside of custom hooks? Line 74 of the RFC states that it can be only used in function components and custom hooks. It only user-facing difference to regular hooks is that you can call it conditionally.

@tobias-tengler let me rephrase;

As per https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#conditionally-suspending-on-data use can be called conditionally but not inside a non-react closures.
As per this comment it isn't going to be ergonomic to create a custom hook with traditional useHook naming that only uses use as then the compiler would enforce regular rules. So yes, you can use it in custom hooks, but if you want to use that custom hook like use then you'll have to ignore the compiler.

To summarise, the point I was raising was how use is kind of a hook and also not.

Compiler would complain if you create a custom hook that only uses  use  as it won't be able to tell whether your custom useUserViaUse hook is a regular hook or one with only use.
use can be used conditionally/in loops but not inside a non-react closure. So now developer has to remember two things for this special hook, i.e. forget regular hooks rule calling it inside condition/loop but at the same time to remember to not call it inside a callback function.

Since the time of writing I encountered this tweet by daniguardio_la of renaming it to asResource, and I like it! Developer only needs to know how and when to use asResource and not confuse it with any of the rules of hooks.

Very interesting RFC, the only thing that comes to my mind about usable, is that it could support cancellation. When rendering, the request continues, and it would be a nice optimization.
Sorry if I completely missed the point of this RFC.

I wouldn't say I like this part:
use has a special ability that other Hooks do not — calls can be nested inside conditions, blocks, and loops.

A little bit of consistency would be nice. Maybe not call it a hook, then?
In the future, If we have more hooks with special abilities, eventually, it will add significant cognitive overhead to the library.

If use gets to break the rules of hooks then why use the use prefix at all? Why not just admit it's a new paradigm with a new prefix like unwrap. So unwrapPromise or unwrapContext or just unwrap if you really do think it's a one-off that could be specifically reimplemented for multiple scenarios.
I am also skeptical of the actual async/await model for SSR. I don't really get what is protecting people from nesting async components like this which could expand server response times because promises will be addressed linearly. I know my example is client-side, but the concept should work the same seeing as in both cases you are suspending the building of the application tree by using await.
To be clear, I recognize that sometimes this model is unavoidable because somtimes you need to linearly chain promises because the second promise requires response data from the first. I just think that this scenario is more likely to reasonably happen on the client than on the server because the server response is stateless.
Maybe avoiding this is a reasonable expectation for developers seeing as you could make this mistake with promises today in JS anyways, but I feel like React's hard stance on not allowing components to be async/await made this a lot harder to do. Obviously, you could even sort-of do it in React today (see my example, notably wouldn't actually work with hooks/props), but this pattern would just make it really easy, where-as I had to go out of my way. Seems like it could be at risk of becoming the next big foot-gun.

@calebmer https://github.com/reactjs/rfcs/pull/229#issuecomment-1279029026

Why not be more strict and say “for good performance promises need to be referentially equal across renders”. Which would change the example to:

All good points. The earlier drafts of this proposal did have that constraint — we've flip flopped a few times because the expectation is that most code will use a framework/library-level helper that is already cached. Or, they will pass promises from a Server Component and unwrap that with use on the client. (Indeed, that's probably the most idiomatic usage of use on the client, and it doesn't suffer from any of the replaying or microtask pitfalls because the promise only changes when the Server Component tree changes: on navigation, refresh, or server mutation.)
So if all your IO is cached, the remaining reason why you'd get a fresh promise is because of an extra async function indirection. And it's quite annoying to have to add a cache call around every wrapper, especially given it's not necessary on the server.
However, maybe that's just something we'll have to live with until we have a compiler to fix this for us.
I think there's a version of this proposal where we punt a bit on the client use case for now, and focus on just making it work well on the server. Things are so much simpler there because there are no user inputs, or updates, AsyncLocalStorage exists, async/await just works... If we do go that route for now, we'd likely still ship use for the "conditionally suspend on a Server Components promise" case, and to replace the unstable throw-a-promise mechanism that currently exists..

Regarding all the naming suggestions, I don't think there's much more to say other than what's already in the RFC. I appreciate the comments saying it could lead to confusion, though.
However, we've tested this with some users to get early feedback and, in general, most people get over the weirdness within a week or so.
The way I encourage people to think about it is that, if React were a language, use would be a keyword like yield or await. (Maybe not that exact one — but we'd address this problem with syntax, is what I'm saying). Yes, it is a bit annoying that we have to replicate features that would ideally be syntactical with naming conventions instead, but we've been through this before when we introduced Hooks and my conclusion over the past few years is that despite some initial groaning, the use- naming convention just hasn't been a big deal in practice. (There are other criticisms of Hooks, but the naming concern isn't among the most pressing.)
Also — and this comparison isn't perfect, since I'm about to bring up a case where we did introduce custom syntax — I think it's like JSX, too. In the sense that it caused lots of controversy at the beginning by contravening the norms of the day, but eventually turned into no big deal.

Also important to remember that there will be a linter plugin that enforces the correct usage of use. It'll be part of the existing Rules of Hooks ESLint package.

If a promise is rejected, how call use(getSomethingWrappedInCache(args)) again with the same parameters? For example, if I want to make a retry button.
const getSomeDataFormApi = cache(async ({ id }) => fetch(`/api/something/${id}`))
const Component = ({ id }) => {
  const data = use(getSomeDataFromApi({ id }))
  return // ...
}

How to retry the request in case if we want to make it with the same parameters? Manually set a new key on the component?

On the previous implementation of the Suspense, entire state of the component was discarded on await (coughts, .read()). This was so bad because you wouldn't able to cache the data using useMemo(() => fetchStore(...), []), because when the .read() completed, the Component itself was freshly rendered without keeping any state, therefore the function inside of the useMemo would be called twice and an infinite loop.
and I really hope use(promise) will not cause the hooks on the top of this call to be refreshed.. But I have a feeling that it will work almost the same way it did before. I do not want to cache the promises on a global object with keys (however I would like React does it for me like as it does for memoizing and keeping component states).
And the proposal says that use() can have conditions unlike other hooks. But then what about useMemo() hook which we may use it conditionally to use use()?
Consuder such an example: (modified from the commit)
function Note({id, shouldIncludeAuthor}) {
  const noteTask = useMemo(() => fetchNote(id), [id]);
  const note = use(noteTask); // 1st question, will noteTask be persistent after resume?
  let byline = null;
  if (shouldIncludeAuthor) {
    // 2nd question, useMemo can not be conditially called therefore promise has to be given to the component from a cache?
    const authorTask = useMemo(() => fetchNoteAuthor(note.authorId), [note.authorId]);
    const author = use(fetchNoteAuthor(note.authorId));
    byline = <h2>{author.displayName}</h2>;
  }
  return (
    <div>
      <h1>{note.title}</h1>
      {byline}
      <section>{note.body}</section>
    </div>
  );
}

So my guess that use() will not work with memoized values returned by useMemo() hook at all, just like the previous Suspense implementations. These promises probably will have to be cached outside of the component somewhere else and this feels so bad to not being able to memoize it with useMemo().

I really appreciate that the React team listened to community feedback and kept iterating on server components. async / await on the server makes a ton of sense of adoption!
I do have a comment on the name use though. I understand this makes the utility generic enough to tackle any value (unwrap a promise, unwrap a React Context, and unwrap other future data types). However, using the name use for something distinct from a React hook feels like a tough learning curve.
Maybe use could be called unwrap instead? This takes after Rust lang's naming for options unwrapping, a functional programming concept that feels similar in spirit.

Maybe use could be called unwrap instead? This takes after Rust lang's naming for options unwrapping, a functional programming concept that feels similar in spirit.

As a Rust and React user, I too like unwrap. It is like you're unwrapping a value, such as something async, or a Context as previously mentioned. use(Context) vs useContext() might be a little confusing.

It's already possible to create custom hooks like usePromise(there is even something with the same name in the Vue ecosystem) or useObservable right now. And if you tried to implemented them by yourself, you can use them with useMemo, just like what @phryneas and @calebmer demonstrated in the thread.
My question here is that your proposal is literally doing CPS transformation in the React component. Why not just provide a common interface to do that? So we can unwrap any monadic value in the future. It will be more powerful than async/await and will make coding with React fun again.

Another thing to consider when offering alternate suggestions: it needs to not only suggest "unwrapping" (which I argue "use" already does), it also needs to indicate somehow that it's a React-only function. What I like about "use" is that it builds on the naming convention established by the other Hooks already: any function that starts with "use" can only be called from a React function (a component or custom Hook).
The way I'd teach this to beginners is:

Any function that starts with "use" is a Hook — it can only be called from a React function (a component or custom Hook)
Except for the function called "use" (no extra words or letters), all other Hooks can only be called at the top level.

I also think it's an advantage if it's short. A similar construct in Svelte is $, which is quite nice. (Maybe in retrospect that's the convention we should have chosen for Hooks, too: functions that start with $.)

That's why I'm also leaning towards use as it's basically like a React-specific word in the JS world these days, whereas other libraries, such as those that implement Rust-like Result or Option types, might also use unwrap.
What are your thoughts on the googleability factor though as I had mentioned?

My question here is that your proposal is literally doing CPS transformations in the React component. Why not just provide a common interface to do that? So we can unwrap any monadic value in the future.

That's not out of the question but it does expand the problem space quite a bit so we'd need to consider it as a separate RFC.
One trick that would allow you to experiment with this in userspace, though: technically, any Thenable object is supported. That is, you can pass any object that implements a then method. The status and value fields also provide an opportunity for cleverness: if the status of the thenable is set to fulfilled, React will use the value without calling then at all (though it still checks for the existence of then to confirm it's a Thenable).

All of it is well thought out and the slightly concerning parts are being talked about already. However, I do want to push back on the reasoning given against generator functions.

The main disadvantage of generators is that you can't use them inside custom hooks without effectively requiring every single Hook to be a generator.

It's true that you won't be able to use yield within regular custom hooks, but you wouldn't need to convert every hook to be a generator, only the hooks that could possibly suspend.
I would argue that this is a strong argument in favour of generator functions. I think a function that can possibly suspend should be made more explicit than other hooks.
A generator function and yield solves this problem. Any custom function that yields a promise would need to be a generator function itself.
This would be separate from hooks.
However, there could also be hooks that also unwrap promises and those would be generator functions with the use* naming scheme.

The existing ESLint rule would ensure that the rules of hooks apply for hooks.
The yield keyword would ensure that a promise is only unwrapped in a generator function.
Any wrapper function that unwraps promises would need to be yield*'ed, making suspension points explicit.

Some example with generator functions:
// The syntax is much closer to async-await
function* Note({id, isEditing}) {
  const note = yield db.posts.get(id);
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
      {isEditing ? <NoteEditor note={note} /> : null}
    </div>
  );
}

// `yield` instead of `use()`
function* Note({id, shouldIncludeAuthor}) {
  const note = yield fetchNote(id);

  let byline = null;

  if (shouldIncludeAuthor) {
    // Because `use` is inside a conditional block, we avoid blocking
    // unncessarily when `shouldIncludeAuthor` is false.
    const author = yield fetchNoteAuthor(note.authorId);
    byline = <h2>{author.displayName}</h2>;
  }

  return (
    <div>
      <h1>{note.title}</h1>
      {byline}
      <section>{note.body}</section>
    </div>
  );
}

// using a possibly suspending function
function* Note({id, isEditing}) {
  const note = yield* iCouldSuspend(id)
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
      {isEditing ? <NoteEditor note={note} /> : null}
    </div>
  );
}

// using a possibly suspending hook
function* Note({id, isEditing}) {
  const [title, body, editorNode] = yield* useSuspendingHook(id)
  return (
    <div>
      <h1>{title}</h1>
      <section>{body}</section>
      {isEditing ? <NoteEditor note={editorNode} /> : null}
    </div>
  );
}

@acdlite https://github.com/reactjs/rfcs/pull/229#issuecomment-1279864465

One trick that would allow you to experiment with this in userspace, though: technically, any Thenable object is supported. That is, you can pass any object that implements a then method. The status and value fields also provide an opportunity for cleverness: if the status of the thenable is set to fulfilled, React will use the value without calling then at all (though it still checks for the existence of then to confirm it's a Thenable).

I understand that technique, but the missing piece for use(observable) in userspace is to trigger re-renders. (i.e. useReducer(c=>c+1,0)[1] without hook order limitation.)

@dai-shi https://github.com/reactjs/rfcs/pull/229#issuecomment-1279888207

I understand that technique, but the missing piece for use(observable) in userspace is to trigger re-renders. (i.e. useReducer(c=>c+1,0)[1] without hook order limitation.)

Yeah that's one you couldn't currently implement in userspace, would have to either implement in React or expose some other primitive

@nmn https://github.com/reactjs/rfcs/pull/229#issuecomment-1279886333

A generator function and yield solves this problem. Any custom function that yields a promise would need to be a generator function itself.

I'm just not convinced it's that much better than use, other it being actual JS syntax. The behavior is essentially unchanged, but now you have to add yield in a bunch more places.
The runtime overhead of generators is a bit better in some cases when resuming after something suspends, but it's worse in the case where nothing suspends, which is more common. And it doesn't fix the microtask pitfall because if you yield an uncached promise you still have to resolve that in a microtask.

The behavior is essentially unchanged, but now you have to add yield in a bunch more places.

TLDR; Agreed! And I think the bunch of extra yields is a feature and not a bug as it makes potential suspension explicit.
And the rules of use are a bit awkward when mixed with the rules of hooks.

Side question, I don't think this was explicitly mentioned in the RFC, but would the use syntax also work on the server?
I'm assuming async-await is only for Server Components, but use would work on both server and client?

The nice thing about generators is they can yield multiple async values. However, if we go with my suggestion of supporting  AsyncIterable, this wouldn't be a problem, as you'd have the option of whether to implement the AsyncIterable with an async generator or something else (like a ReadableStream, which is already used by fetch, which React already recommends using).

That's not out of the question but it does expand the problem space quite a bit so we'd need to consider it as a separate RFC.

Ok. Thank you.

One trick that would allow you to experiment with this in userspace, though: technically, any Thenable object is supported. That is, you can pass any object that implements a then method. The status and value fields also provide an opportunity for cleverness: if the status of the thenable is set to fulfilled, React will use the value without calling then at all (though it still checks for the existence of then to confirm it's a Thenable).

This might be too clever.

One things to add about yield that speaks against it:
It's a lot of mental overload to teach to beginners. Most people in the ecosystem don't know what a generator function is, it is just some arcane syntax to them. It was a pain with sagas and it will be a pain again.
The number of StackOverflow questions for Uncaught SyntaxError: yield expression is only valid in generators will explode.
People will start adding * to every second function they write without knowing if they need it just to be sure.
That will in return break all kinds of things in other places - especially if they are using only JavaScript and not TypeScript.

One things to add about yield that speaks against it:
It's a lot of mental overload to teach to beginners. Most people in the ecosystem don't know what a generator function is, it is just some arcane syntax to them. It was a pain with sagas and it will be a pain again.
The number of StackOverflow questions for Uncaught SyntaxError: yield expression is only valid in generators will explode.
People will start adding * to every second function they write without knowing if they need it just to be sure.
That will in return break all kinds of things in other places - especially if they are using only JavaScript and not TypeScript.

Maybe instead of learning a framework specific "async but not async" function (which is also confusing for beginners; because how in the world that thing looks like sync but does an async thing? Even a non-beginner would be confused because they need to understand what React does in the background); at least people would use something in the language? yield can be only used in generator functions and await can be only used async functions. There are a lot of similar questions about async functions in stackoverflow too.
Problem with sagas were that they were extremely powerful and flexible with a very big API surface. So it was very daunting at first to get going with them.
People keep suggesting generators because what React seems to need at the very high level is start, stop, continue piece of work with high amount of control. This immediately suggests generators at first. I get that upon closer inspection they are also not perfect and have short comings for React's exact use-cases. But I think having the discussion about them out in the open right now is very important for both making sure we are not leaving any door open and also laying the groundwork of potential future ideas.
Maybe not this one but a feature version of React will use generators when there are more features built in to the language about them.

My point is that you do not need async generator functions to implement AsyncIterable, and that would be a very useful API to have supported in React. This is already standardized, and Node already implements it.

Maybe instead of learning a framework specific "async but not async" function (which is also confusing for beginners; because how in the world that thing looks like sync but does an async thing? Even a non-beginner would be confused because they need to understand what React does in the background); at least people would use something in the language? yield can be only used in generator functions and await can be only used async functions. There are a lot of similar questions about async functions in stackoverflow too.

To explain my perspective a bit here: in the last 2 years I have answered about 1k questions mostly from beginners on StackOverflow and I have spent hundreds of hours in the Reactiflux #redux and #redux-saga channels helping all kinds of people - among them a lot of people just coming from bootcamps or just learning JavaScript.
I am worried about these people if we introduce generators as a standard concept.
You know, most beginners don't ask "because how in the world that thing looks like sync but does an async thing?".
There is already too much going on to even get to that question.
Many (I'd almost say most) people getting into React don't have that concept of "this component will execute at multiple points in time".
They write a useEffect and in the same render try to access the result. And then they try to fix it for hours without knowing what to google or ask for.
use has the great potential to make that actually possible and remove that confusion.
To someone just learning React, that's not an additional concept to learn, but it hides a lot of concepts that might distract them from what they have to learn on top: JavaScript, HTML, React, maybe programming in general.
I'd say most people in their first one or two years don't get down to the nuances of asynchronity or "thinking in timelines" when it comes to programming. A lot of people don't really even understand the syntax required for that within that time.
It is super common that I see code bases than use await and then in the same function, often in the same line, but not as an explicit design decision, but because people do not realize that these are a different syntax for the same thing.
They got one syntax from one tutorial and another from another. Neither tutorial explained the thing.
Both kinda seem to work. So it's copy-pasted together.
I have talked to so many people about sagas and so few of those really understood the implications of the syntax they were working with. It was just "that weird saga syntax they have to use".
I don't want to bash anyone here - all that is normal.
There are tons of concepts to understand. Meanwhile, those people don't learn in a vacuum - they have to get stuff done. They want to get paid.
Many are not mentored or under pressure.
I am telling this because I want to create empathy for those new learners. It's just too easy to forget how that feels if you are 5, 10 or 20 years in the business already.
Now, with all that as a baseline, add generators on top of that, which per nature are - like promises - kinda "infectious". I can imagine a codebase going from "needing two generator" functions to "all functions are generators now" in a spell of frustration. And that will probably happen a lot of times.
Yes, generators are a language feature. But they are not used a lot in the ecosystem. They are an edge-case feature, and that for good reason: they are incredibly powerful and can be incredibly confusing. I have a full conference talk about generators and it takes an hour to get through and it doesn't even cover all features.
Now, on the other hand: someone who is in the situation that they are asking that "because how in the world that thing looks like sync but does an async thing?" question - they are beyond that step.

Adding one thing on top of all that:
generators are horrible to use with TypeScript
It is very hard to get the result of a yield typed correctly. Introducing sagas has the risk of eliminating a ton of type safety in applications and introducing any left and right.
If the React team is entertaining the idea of adding sagas, please evaluate if the use cases you are aiming for are usable well with TypeScript without losing all type safety.

On a positive note - I just took a stab at implementing this for RTK Query and just from looking at the code changes (without actually trying it), this is mostly moving code around - almost writing no new code. It is probably the cleanest attempt at suspense support we've made so far.
https://github.com/reduxjs/redux-toolkit/pull/2781

There are still open questions like "how would that look like with in-component query waterfalls?" and we would probably have to provide some kind of "memoizing Promise.all" for parallel suspending queries.
const { data1, data2, data3 } = use(memoPromiseAll({
  data1: useSomeData(arg1),
  data2: useSomeMoreData(arg2),
  data3: useEvenModeData(arg3)
}))

But maybe even use could come with something like that in the long run, so it might become
const { data1, data2, data3 } = use({
  data1: useSomeData(arg1),
  data2: useSomeMoreData(arg2),
  data3: useEvenModeData(arg3)
))

or a similar useAll hook:
const { data1, data2, data3 } = useAll({
  data1: useSomeData(arg1),
  data2: useSomeMoreData(arg2),
  data3: useEvenModeData(arg3)
))

What this also does not take into account right now is "what if that cache value refreshes". use will always reference that one promise - so maybe for that we would either have to trick around in the useQuery hook itself and wiggle some data from useQueryState in - or we would have to fall back to a separate implementation.

The way I'd encourage thinking about it (someone correct me if I'm wrong) is that what you want to expose are data fetching functions themselves, just like with async/await. So there's no need for anything special to make it parallel.
const promise1 = fetchData1()
const promise2 = fetchData2()
// these are already parallel by now!
const data1 = use(promise1)
const data2 = use(promise2)

@gaearon the idea here was to abuse the fact that use calls .then a bit.
The useSomeQuery hook could be called without use and keep the normal non-suspense behaviour, or with use and be suspendable.
So both of these would be equally valid:
// this one will subscribe to the cache (and start a request if necessary) in a `useEffect`
const { isLoading, isSuccess, isError, data, error } = useSomeQuery(arg)

// this one will subscribe to the cache (and start a request if necessary) once `use` calls `.then` on it
const data = use(useSomeQuery(arg))

This would make migration over to suspense (or just experimenting with it) a lot more approachable, since it could be done gradually without shipping a lot of extra code from our side.
Since in case of the "non-suspense behaviour", we don't want to trigger any kind of data fetching during render, that "subscribe to the cache and potentially start fetching" part would only happen once use calls .then - with the downside that that would again potentially introduce an in-component waterfall.
Of course, we could alternatively also create a second hook that always does this "subscribe/fetch" immediately during render, but if we can get away with having the 2-in-1, that would likely be ideal.

The way I'd encourage thinking about it (someone correct me if I'm wrong) is that what you want to expose are data fetching functions themselves, just like with async/await. So there's no need for anything special to make it parallel.
const promise1 = fetchData1()
const promise2 = fetchData2()
// these are already parallel by now!
const data1 = use(promise1)
const data2 = use(promise2)

There is no need for Promise.all or similar. Even outside React, Promise.all is unnecessary for parallel IO as long as you remember not to await immediately but let everything start first.

This is not something that you would write in a normal async function though. Everyone would use Promise.all. And Server-Side Components are async functions. Saying this because not sure about this discrepency is also intentional or a minus point from the perspective of "following idiomatic JS patterns".
I guess one can write a useAll like thing themselves or have a fetchAllData function that calls fetchData1 & fetchData2 with a bit more idiomatic JavaScript (with Promise.all and all the other goodies)?

This is not something that you would write in a normal async function though. Everyone would use Promise.all. And Server-Side Components are async functions. Saying this because not sure about this discrepency is also intentional or a minus point from the perspective of "following idiomatic JS patterns".

I don't think there's anything in JS itself that makes it unidiomatic. The language makes this pattern very easy to do. The reason many people aren't exposed to this is because it's not being taught widely. We have some leverage regarding which patterns are being taught widely, so I wouldn't take "people don't know you can do this in JS" as a hard constraint.

Re: https://github.com/reactjs/rfcs/pull/229#issuecomment-1279983524
This might be a taste thing but personally I find mixing two patterns like this more confusing than helpful. As a consumer I would prefer a clear delienation between these two different API styles. From my perspective, mixing them isn't ideal because people will attempt reading promise.isLoading instead of Suspense, etc. I'm curious what @acdlite thinks.

@phryneas

I agree what you say about generators (especially Type Safety and all that). The idea here was not saying "let's freakin use them"; it's more about challenging the current ideas in the RFC for the sake of challenging them to be able to see any kind of gaps in the suggested approach.

React's mental model was really simple at the beginning and it played a huge role in it's adoption imho. I feel like we have passed that stage now when we started talking about concurrent rendering, time slicing, effects, dependency arrays, stale closures etc. I understand that this RFC aims to simplify things on the user-land but still the mental model of React remains to be a lot more complicated at the same time (which is a problem too). Asymmetries in the API and one time exceptions to the rules etc. makes people a bit nervous or cautious. That is also something to empathise with that might affect almost everyone, not just people who came out of bootcamps. Because those are the problems that will stay around if things won't turn out to be as hoped or promised.

Re: #229 (comment)
This might be a taste thing but personally I find mixing two patterns like this more confusing than helpful. As a consumer I would prefer a clear delienation between these two different API styles. From my perspective, mixing them isn't ideal because people will attempt reading promise.isLoading instead of Suspense, etc. I'm curious what @acdlite thinks.

Well, reading result.isLoading is the correct way to use it now before we add suspense, so I don't see how that would ever be harmful.
Currently, the fields are

      const {
        isUninitialized,
        isLoading,
        isFetching,
        isSuccess,
        isError,
        refetch,
        currentData,
        data,
        endpointName,
        error,
        fulfilledTimeStamp,
        originalArgs,
        requestId,
        startedTimeStamp,
        status,
      } = useQuery()

Either use would be perfectly valid, so accessing result.isLoading is very much possible - it will just always be false if used with use (assuming the render completes).
Essentially, we could just add value (= currentData  or data), reason (= error) and then (new function that immediately triggers a cache lookup/fetch) to the hook and it would be fine to use suspense or non-suspense.

This is not something that you would write in a normal async function though. Everyone would use Promise.all. And Server-Side Components are async functions. Saying this because not sure about this discrepency is also intentional or a minus point from the perspective of "following idiomatic JS patterns".

I don't think there's anything in JS itself that makes it unidiomatic. The language makes this pattern very easy to do. The reason many people aren't exposed to this is because it's not being taught widely. We have some leverage regarding which patterns are being taught widely, so I wouldn't take "people don't know you can do this in JS" as a hard constraint.

I think the language allows a lot of things; and idiomatic parts are a subset of them.
Yes I agree that there's no consortium about what is idiomatic and what is not but this one is not that hard to speculate.
Gather different people from different levels of experience and background who write JS and ask them to make 2 fetch requests in parallel; I don' think they would ever do:
const promise1 = fetch1()
const promise2 = fetch2()
await promise1
await promise2

At least without an additional requirement involving doing something about the individual operations themselves. And I don't believe they would start doing this outside of client-side components (yes not even in the server-side components) regardless of React's impact. I will definitely check this as a one-sided bet when this gets official (and pat myself in the back if I am right) 😄
I know it's not the biggest problem in the world, just an observation about the difference between use & await and the patterns around them.
Sorry for the bikeshedding.

I wanted to copy a conversation here that I've had with @phryneas and @TKDodo on Twitter.
https://twitter.com/tomus_sherman/status/1581722370418835458
TL;DR: use(observable) is the only thing needed here to bring my personal preferred API for using React Query/RTKQ abstractions with what's proposed in this RFC.
Those libraries simply need to supply an observable-like API that can be passed to use(). In React Query's case, it already has a QueryObserver which can be transformed into an observable in user land already. Something first party in react query, assuming use(observable) support, would look a bit like this:
const { data } = use(queryClient.observeQuery());

I wanted to copy a conversation here that I've had with @phryneas and @TKDodo on Twitter.
https://twitter.com/tomus_sherman/status/1581722370418835458
TL;DR: use(observable) is the only thing needed here to bring my personal preferred API for using React Query/RTKQ abstractions with what's proposed in this RFC.
Those libraries simply need to supply an observable-like API that can be passed to use(). In React Query's case, it already has a QueryObserver which can be transformed into an observable in user land already. Something first party in react query, assuming use(observable) support, would look a bit like this:
const { data } = use(queryClient.observeQuery());

Just to mirror the reasoning for that here: for us it is important to track which components are currently using a certain cache entry so we can decide to keep or collect it.
That is currently not possible without hooks since we need hooks to track unsubscribes - which we can only really do in a useEffect cleanup. Having the ability to use observables here that would be subscribed & unsubscribed by use would allow us to work without hooks, which would unlock a lot of potential.

I think it's unlikely that something like the lifetime of something in use(observable) would be tied to the component unfortunately. It would be tied to the Cache and the use(...) would only work on a "hot" observable.

Ok, that's a shame. Guess we'll shelve this discussion until the Cache RFC drops then 👍

How would use deal with cancellation while a component is in Suspense?
For example:
function App() {
   const [isShown, setIsShown] = useState(true);

   return <div>
    <button onClick={() => setIsShown(s => !s)}>Toggle</button>
    <Suspense fallback="Loading">
      { isShown ? <History /> : null }
    </Suspense>
   </div>
}

function History() {
   const historyList = use(fetchUserHistory()) // Long request

   return historyList.map(...)
}

If while <History> is loading the user clicks on "Toggle", React will cancel the rendering of History. It will not unmount it, because it was never mounted, so this can't be solved with a useEffect.
Is there any way that use can notify that the component will never mount, so that the fetching library can cancel the request if needed?

My initial reaction to this is similar to the one from https://github.com/reactjs/rfcs/pull/229#issuecomment-1279729390
If this doesn't follow the existing rules of hooks, perhaps it isn't useful to call it a hook, and instead it should be treated as a separate concept? (what the function might then be called would follow from how the concept is described)
On a more minor note, I think it would be worth being explicit in the RFC about what happens if the promise rejects - I assume the use() call throws on the second time around and then it's up to you to either catch it or let the render fail?

Is use up for bikeshedding?
Thoughts on resource(source) or asResource(source) or something other than use?

I think I may be of a silent proportion of people here that really like the naming.
I think saying things like "it's now more difficult to remember the rules" is, while correct on some level, irrelevant in practice.
There is one thing that makes use() different to other hooks and that's that it can be called conditionally and in loops. This is a technical constraint imposed on current hooks, it's not an intrinsic property of them. Articles, resources, coaches (I'm guilty of this) had pedelled this constraint as a feature in the past which is simply wrong. In the future a compiler could remove this constraint for other hooks, too.
So use() removes one of the biggest technical constraints of other hooks, in my mind this makes it the most "hook-like" hook that exists as it's closer to the original design goals. Through this lens, and backed up by the fact it will be the only hook (outside of a compiler) that has the constraint removed, the other hooks can be seen as the exception that makes the rule.

In the future, we could unlock general support for async Client Components by compiling them to a specialized, generator-like runtime.

If this is already the plan, what's stopping us from creating the required compiler now and ship async-await directly and sidestep the use API entirely?
async-await is a more ideal API because:

It unifies the API with the server
The plan is to implement it in development anway (with a warning)
It doesn't require learning a new API with new rules. (language feature)
Makes possible suspension explicit
Compile-step gives us the ideal implementation.

React documentation is great. And it states that

A custom Hook is a JavaScript function whose name starts with ”use” and that may call

So naming this function as "use" will cause a conflict. I support those comments which brings up new naming ideas such as "consume".

@slorber

something I'm concerned about: introducing a production-only compiler to compile to generators makes things more complicated and different in dev vs prod

the compiler wouldn’t be production only. Instead it would be a dev and prod compiler (similar to JSX).
However, async-await would still work with warnings without the compile step.
——
I like this approach because while generators are the right language level feature, the problems with types are serious enough and go away completely with async-await.

wow nice

I think expanding the React API surface area to address certain shortcomings of the way React works is a step in the wrong direction making React even more complex and obscure. We all know how well this approach of not really solving the problem played out with the invention of useCallback and useMemo - we had to wait a few years for it to be actually solved where it needs to be solved in the compiler. Investing in the compiler and relying on JS language features seems to me the winning strategy for the long term.
And if the use monstrosity is about to stay, at least let us name it better, like useMonstrosity or something.

I like the separation between initiating a Promise and reading from it. However, in order to avoid render-as-fetch "waterfalls" we need to hoist the initiation of promises up to the top of component trees.

One benefit of this is that at the type-level, our components express their dependence on asynchronous data. It's quite nice that you can look at the TypeScript props of a component and see a Promise<ResponseType> and immediately know that a component needs to be within a Suspense boundary.
On the other hand, passing down Promises leads us back to "prop drilling" (unless we start storing our promises within React context). And, more problematically, there is the possibility that if we pass a promise down the tree but forget to use it, we could end up with unhandled promise rejections. I can imagine running into this if we've used the promises conditionally and didn't run into any rejections during development.

Presumably the existence of a cache avoids the need to memoize the initation of a promise? I think it'd be a foot gun if memoization was used not as a performance improvement but to control the initiation of promises. The dependency checking of useMemo and useEffect is fragile to accidental object creation and hard to debug (for both beginners and experienced developers).
I'm a user of Recoil and therefore have a few thoughts over how easy it is to accidentally make code that is sequential.

For example:
const fetchA = cache(() => fetch(...));
const fetchB = cache(() => fetch(...));

function Parent() {
  const p = fetchA();
  const value = use(p);
  return (
    <>
      <div>{value}</div>
      <Child />
    </>
  );
}

function Child() {
  const p = fetchB();
  const value = use(p);
  return (
    <>
      <div>{value}</div>
    </>
  );
}

Am I right in thinking that <Parent /> immediately suspends by throwing the Promise returned by fetchA() and therefore <Child /> can't be rendered until after it is resolved?
It's a shame that it couldn't render both the parent and its children, while collecting all of the uses and only suspending once it knows about all of them...
I guess a reason this isn't possible is that we might have an if-block which depends on a result of a use(p), or that we might do something like apply a transformation and therefore the value has to exist for this to be possible before passing it into the JSX.
Was there any thought about the possibility of skipping use(p) and just allowing us to directly pass a Promise into our JSX? For example, <div>{p}</div> could be more lazy and avoid suspending until it is absolutely necesssary to do so. One issue I can see with this, is that, if you want derived data you're either forced into hoisting the transformation into the creation of your promise (which is bad if you think co-location is a good principle), or doing something like this <div>{p.then(transform)}</div> which is noisey. (I guess if use(p) is the way forwards we just write transform(use(p)).)
Other thoughts -- sometimes with Recoil, you avoid using suspense boundaries and instead use something like useRecoilValueLoadable to get an object like { state: 'hasValue' | 'hasError' | 'loading', contents: T | Error }. In practice, this is useful when you have something like a submit button which is disabled while an asynchronous operation completes. I don't know whether this is possible with use?
e.g.
function ValidationMessage({ p }) {
  return (
    <>
      {use(p)}
    </>
  );
}

function Form({ value, handleChange }) {
  const p = fetchA(value);
  return (
    <form method="get" action="/search">
      <div>
        <label>Search <input name="term" type="text" value={value} onChange={handleChange} /></label>
        <Suspense fallback={<Spinner />}>
          <ValidationMessage p={p} />
        </Suspense>
      </div>
      <button type="submit" disabled={p.status === 'pending' || p.status === 'rejected'}>Search</button>
    </form>
  );
}

Is that possible? How is this meant to work? (Sorry for naff example.)

What happens in the case that the promise inside use(promise) fails or raises an exception?
A typical example of this would be when axios receives a 4XX value in the response header, it immediately raises an error. Would we have to bake our try-catch blocks into the promises such that they always return a value?
The same goes for a 'loading' state as well.
Having in-built error and loading states would be incredibly useful for this proposal, as it would reduce the amount of wrapper code we will inevitabley write around these use() statements...
E.g.
[data, isResolved, isRejected] = use(promise);

@zachnicoll if you were to manually catch there, that would ruin the flow of React. You should handle errors in React with an error boundary.

@phryneas This led to an interesting discussion thread on twitter actually. Seemed that, since use is directly compared to promises, some would expect the try / catch pattern common in server-side JS to work in client-side components. I agree that error and suspense boundaries make sense for error and loading fallbacks, but that seems like a pretty big mental shift between server and client. Unless server components should not use try / catch either? And if so, how can React steer users to that pit of success?

What happens in the case that the promise inside use(promise) fails or raises an exception?
A typical example of this would be when axios receives a 4XX value in the response header, it immediately raises an error. Would we have to bake our try-catch blocks into the promises such that they always return a value?
The same goes for a 'loading' state as well.
Having in-built error and loading states would be incredibly useful for this proposal, as it would reduce the amount of wrapper code we will inevitabley write around these use() statements...
E.g.
[data, isResolved, isRejected] = use(promise);

@zachnicoll
My understanding is when a promise is wrapped with use, React will extend the Promise object with 3 properties roughly typed as
{
 status: 'pending' | 'fulfilled' | 'rejected';
 value: any;
 reason?: Error;
}

The reason would be the value returned by your Promise.reject. Someone smarter than I can verify this. The RFC doesn't explicitly mention any use of Promise.catch.

I think meaning of the word 'use' is getting broader and broader.
await means it'll await for the response(-like) things.
yield means it'll yield something.
but use- means "it is a function called 'hook' which is only available in a React component".
and now the independent word use would add another meaning "it is yet a hook but you can use it in a conditional statement too".
it's also a bit weird that use hook acts fundamentally different with other use- family while it's name sounds like a mother/father of all the other use-prefixed hooks. If you're going to use use word, it would better share common characteristics with use- prefixed hooks. If it has to work differently, we may better use another word to prevent confusion.

@acdlite

const promise = useQuery(...);
const data = use(promise);

feels like a very clunky user-facing API, so I don't think it will catch on.

In practice we expect it would look more like this:

const data = use(fetchQuery(...));

From the react-query viewpoint, right now, we really need the hook useQuery to be called inside the component, because it connects to the cache with useSyncExternalStore to create the subscription. If we just pass a promise to use, it might unwrap it successfully, but it would not subscribe to the cache and notify the component of further changes.
So I'm seeing two approaches currently:

What we are doing now: a suspense: boolean flag to conditionally suspend, or maybe a separate useSuspenseQuery hook. The boolean flag itself is not so nice for TypeScript because it will be hard to make data not include undefined on type level. The separate hook is okay but a bit clunky.

I don't think use(useQuery(..)) is a bad api. It basically shifts all the suspense things into user-land. It could also be:

// normal useQuery with all the things returned + promise
// data is T | undefined
const { isLoading, data, error, promise } = useQuery(...)

// realData will not be undefined
const realData = use(promise)

Users could decide to suspend when they want, even conditionally, or not at all. This would also go well with conditional queries:
const query = useQuery({ queryKey, queryFn, enabled: someCondition })

// suspends as soon as query becomes enabled
if (someCondition) {
  const data = use(query.promise)
})

Unless we go with what @phryneas and @tom-sherman proposed where use would accept an observable and basically be tied to a component lifecycle, this feels like the best approach atm...

Why not call the hook useAsync() instead of use()?

10x clearer.

Aligns well with async function() {}.

The name use() suggests that it's some kind of special "mother of all" hooks.

I can't think of a reason why use() would be a better name?
(Loving the RFC otherwise 😍.)

Why not call the hook useAsync() instead of use()?

10x clearer what the hook is about.
Aligns well with async function() {}.
Aligns better with other hook names useState(), useEffect(), ...
The name use() suggests that it's some kind of special "mother of all" hooks.

I can't think of a reason why use() would be a better name?
(Loving the RFC otherwise 😍.)

Why not call the hook useAsync() instead of use()?

10x clearer what the hook is about.
Aligns well with async function() {}.
Aligns better with other hook names useState(), useEffect(), ...
The name use() suggests that it's some kind of special "mother of all" hooks.

I can't think of a reason why use() would be a better name?
(Loving the RFC otherwise 😍.)

Ah, just seing this:

Why isn't use called something more specific?

Promises are not the only "usable" type — for example, you'll also be able to be use(Context).

Apologies for the spam 😅.

I'm just not convinced it's that much better than use, other it being actual JS syntax. The behavior is essentially unchanged, but now you have to add yield in a bunch more places.
The runtime overhead of generators is a bit better in some cases when resuming after something suspends, but it's worse in the case where nothing suspends, which is more common. And it doesn't fix the microtask pitfall because if you yield an uncached promise you still have to resolve that in a microtask.

It does fix the microtask pitfall in one key way: the React runtime is now the one driving the generator, not the microtask queue, and thus it is able to implement its dynamic scoping for hooks even across a yield. An async server component declared as a function* that yields promises (instead of async/await) would still be able to safely use hooks.
The main caveat I'd add to using function* is how difficult they are to type in TypeScript. There are (badly documented) methods, see https://github.com/microsoft/TypeScript/pull/30790 for more details, but I haven't checked if they're sufficient for React's use case.

This seems like a huge update. Is this committed to be part of React 18 or is it possible it may be part of an eventual React 19 release?

Great news, the feature from $mol from 2016 is finally getting to React. True, in a rather crooked form, nailed to templates, and with a bunch of restrictions. Why not already take $mod_wire and stop introducing new API every year? Here is an example of a model and a view from there:
export class GitHub extends Object {

	@mems static issue( value: number, reload?: "reload" ) {

		sleep(500) // for debounce

		return getJSON( `https://api.github.com/repos/nin-jin/HabHub/issues/${value}` ) as {
			title: string
			html_url: string
		}

	}

}

export class Counter extends Component<Counter> {

  @mem numb(value = 48) {
    return value;
  }

  issue(reload?: "reload") {
    return GitHub.issue(this.numb(), reload);
  }

  title() {
    return this.issue().title;
  }

  link() {
    return this.issue().html_url;
  }

  compose() {
    return (
      <div id={this.id} className="counter">

        <InputNumber
          id={`${this.id}-numb`}
          numb={(next) => this.numb(next)} // hack to lift state up
        />

        <Safe
          id={`${this.id}-title-safe`}
          task={() => (
            <a
              id={`${this.id}-title`}
              className="counter-title"
              href={this.link()}
            >
              {this.title()}
            </a>
          )}
        />

        <Button
          id={`${this.id}-reload`}
          action={() => this.issue("reload")}
          title={() => "Reload"}
        />

      </div>
    );
  }

}

@mdmathewdc , it's mentioned in the RFC document
https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#seamless-integration-with-javascript-ecosystem
We were frequently asked: why not use async/await? Originally, we wanted to have a consistent API for accessing data across Server Components, Client Components, and Shared Components. However, we're unable to support async/await in Client Components for technical reasons we'll cover in a later section.

@mdmathewdc , it's mentioned in the full RFC document. (I'm not saying I agree on the name use though 😅)
https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#why-isnt-use-called-something-more-specific

Why isn't use called something more specific?
Promises are not the only "usable" type — for example, you'll also be able to be use(Context).
use is a very special Hook because it's allowed be called conditionally. The idea is to mitigate confusion by making it the only conditional Hook. Instead of remembering a handful of different conditional Hooks, developers will only have to remember one.

@acdlite , it might be better for you to mention about "why we don't use useAwait" stuff in your PR main comment (maybe a link to the related section in your RFC), because people who skips the whole long discussion keeps asking about "why not useAwait". I'm not saying it is wrong to suggest useAwait, but people should aware of how much we've discussed about that name so far.

https://github.com/reactjs/rfcs/pull/229#issuecomment-1278322375

Yea, no that's a limitation. They have to be Hooks or you have to read it on the outside and pass it in. If Hooks could be made conditional and used in some loops, then it becomes less limiting though.

Looking at https://github.com/facebook/react/pull/25634, again I think it would unlimit the use case, if deriving usables were allowed: creating a new usable with existing usables plus some logic, which can be called "custom usables"

Unlike use, the reconciler will recursively unwrap the value until it reaches a non-Usable type, e.g. Usable<Usable> will resolve to T.

I wonder why use doesn't unwrap the value recursively.
Just my two cents. Maybe out of the scope of this RFC.

The name use is going to be a nightmare for new developers searching for information.

In the $mol_wire we are using sync function to convert async API to sync (and async for vice versa). For React synchronize may be preferable:
const data = synchronize( () => myAPI.fetch(id) )

const syncAPI = synchronize( myAPI )
const data = syncAPI.get(id)
syncAPI.send(id, 'xxx')

Though it's not strictly within the scope of this proposal, use will eventually support additional types besides promises.

Other than Context, what are the types you consider? Knowing that would help the community to propose an alternative to the name "use".

use(Context) will behave identically to useContext(Context), except that it can be called conditionally.

instead of having to learn about a handful of Hooks that are exempted from the typical rules, developers will only have to remember one.

Personally, I'd find it much simpler to remember that useAsync() and useContext() can be conditional, instead of some new "weird" generic hook that I have to wrap my head around. I'll also be confused about whether I should use(Context) or useContext(Context). Even if things clear up over time, I can see some confusion to remain, at least for new React users.
I find it very clear when each hook serves a specific use case. It's easy to remember and to wrap my head around. Having a new generic hook with overlapping use cases adds more confusion than rememberability, I think.
Would it be possible to change useContext() to make it a conditional hook? I don't think that would be a breaking change.
Maybe a safe approach to this is to first ship useAsync() and make useContext() conditional, and later see whether a new hook completely replacing a bunch of hooks could make sense.

My thought that may help you to implement server components and data fetching on server side easier.
SSR Hook (Works well in my projects)

Create hook with only primitives (strings, numbers) in dependencies
Enable this hook on SSR
Pass list of all hooks' dependencies from SSR to client side manually or automatically
During hydrate skip hooks execution if deps not changed.

Cache

Keep idea that all should be changed based on hooks' deps
Use native browsers' cache based on Cache Control header
Do not try to solve problems when you want to keep the tab state if user switching between several of them. I can do it with React context on the level above.

Server Components
Goal — reduce a large static JSON data object transfer. We can't do it now because hydrate need all data. So hydrate should know what component or part of the JSX should be used as static HTML. So let's wrap our static JSX, like return function Comp() { return ssc(<div>server component </div>)} or by specific component like React.lazy but React.server(<div>I'm also static markup</div>)
In this case I'll be able to combine static and dynamic parts inside one component.
I didn't touch here dynamic server components that can be partially refetched from server, because I don't see know this as biggest pain point, but I think it's also possible to achieve.

I wonder how use hook works conditionally.
I know other use~ hook is referenced via array index in every render,
but if use hook is out of this rule,  how this hook implemented? using Map? Set?

What if I, as a library author, want to make a custom hook that only uses this Hook? That will also make my custom hook behave exactly like use, and now I have to face the same naming dilemma, what do I name my custom hook to help my users know that it can break the rules of hooks?
I understand that this Hook should have a unique name to help teach devs that it also behaves differently than other Hooks. IMO the "hooks" concept and the use* naming convention should be split into two different things.

@maxizipitria I might be mistaken, but a 'custom hook' that only uses use would just be an async function (or one that returns a Promise).
The downside would be that, instead of naming it useAwesomeThing you would name it awesomeThing and it would used as use(awesomeThing) instead of useAwesomeThing(). Same letters and same number of parentheses, different casing and positioning.

I have the understanding that you will receive a value when it is fulfilled, otherwise, it mentions it throws and "replays" the rendering when it finally fulfills.
The text mentions you can only use it inside components, it also mentions not using generators because everything would need to be a generator... so... while you might be able to use use inside a custom hook, it might not let you use the custom hook anywhere you could use use.
Considering this, a lib author might expose a promise and the user would have to use(libPromise).

If we are wrapping context as well with use() , it would be better if we suggest a better naming convention as well as an array dependency

A limitation of async Server Components is that they cannot access Hooks

By extension, does this mean that async components do not have a way to access server context (createServerContext) short of rewriting as or wrapping in a regular component?

The name use is going to be a nightmare for new developers searching for information.
They are going to Google "react use hook" or "use react", and they are going to find nothing.
Please call this something else. Anything else.

Just want to echo this again to make it critical.  "use" is an extremely common term in daily searching.
"how to use/learn react" "how to use react hook", "use react hook", react how to use XXX
"react use hook"? "react usehook"?, "use react use"? "use react use hook"? react use use? react use use hook?
This can unintentionally pollute the existing well-established React knowledge map in search engine.

The name use is going to be a nightmare for new developers searching for information.
They are going to Google "react use hook" or "use react", and they are going to find nothing.
Please call this something else. Anything else.

Just want to echo this again to make it critical. "use" is an extremely common term in daily searching. Even from the existing useHook().
"how to use/learn react" "how to use react hook", "use react hook", react how to use XXX
"react use hook"? "react usehook"?, "use react use"? "use react use hook"? react use? react use use? react use use hook? react use function ?
This can unintentionally pollute the existing well-established React knowledge map in search engine. You just cannot expect user to use advanced google key word search all the time. (Even in my own sentence I used "use" multiple times disappointed)

It's a valid point right here, seems like this hook was named so without even considering the new developers who are still learning React. Only a mid level or senior React developer will understand the use hook if it is named so.

Not sure if this was asked before, but is it intended that use can be called from within an async Server Component? In my opinion, for generic library code that can be executed either in Server or Client Components this would be really helpful to enable a unified API.
I noticed this currently throws an error in Next.js: https://github.com/vercel/next.js/issues/44778. Note that use can be called from within a Server Component, just not if it is marked with async.
import {use} from 'react';

// ✅ Works
export default function ServerComponent() {
  return use(Promise.resolve('Hello'));
}

// ❌ Breaks
export default async function ServerComponent() {
  return use(Promise.resolve('Hello'));
}

I understand that async/await is generally preferred for server components, the point here is having library code just work in Server Components with use being an implementation detail somewhere in library code.

Not sure if this was asked before, but is it intended that use can be called from within an async Server Component? In my opinion, for generic library code that can be executed either in Server or Client Components this would be really helpful to enable a unified API.
I noticed this currently throws an error in Next.js: vercel/next.js#44778. Note that use can be called from within a Server Component, just not if it is marked with async.
import {use} from 'react';

// ✅ Works
export default function ServerComponent() {
  return use(Promise.resolve('Hello'));
}

// ❌ Breaks
export default async function ServerComponent() {
  return use(Promise.resolve('Hello'));
}

I understand that async/await is generally preferred for server components, the point here is having library code just work in Server Components with use being an implementation detail somewhere in library code.

As I know, nextjs version 13's  server component can't be used with hooks. Isn't that error pointing that?

As I know, nextjs version 13's server component can't be used with hooks. Isn't that error pointing that?

Hmm, which hooks are you referring to? E.g. useMemo works fine for me. I guess most hooks don't make sense in Server Components (e.g. useState), so I don't expect them to work.

With @acdlite leaving Meta, does that mean this pull request will be deferred until after NextJS 13 and the server components release is really wrapped and released? A while back @gaearon said here https://github.com/vercel/swr/issues/1906#issuecomment-1135166847 that "server components will release before client side suspense and data fetching will happen in non relay data libraries (or at least that is how I interpret it).
Would it be a correct assumption to assume that Suspense support on the client side with a hook like "use()" in this proposal is not coming out until after Next.js Server components have shipped?

I can't tell you anything about release timeline, but I've noticed this demo is already consuming a use hook from an experimental React build: https://github.com/reactjs/server-components-demo

Since it took me a bit to find it the above experimental repo, here is the link to where use is being called https://github.com/reactjs/server-components-demo/blob/99e49cbb37a6e73aaa0d0751d7cee9217c263c26/src/framework/router.js#L69

I'd like to reiterate the concern on cancellation: https://github.com/reactjs/rfcs/pull/229#issuecomment-1280803906
I think this is a pretty big concern, since use can theoretically start subscriptions that on some cases would never get cleaned up, with the current proposal.
It's actually one of the reasons why currently "the render function must be kept side-effect free". use will break this assumption, but then I think it needs to give some mechanism to trigger cancellation in case the component never gets mounted.

Any update about this RFC?

Apologies for asking, but is there any update on this RFC? It was introduced nearly a year ago, and yet it hasn't been merged and/or included in a stable version of React.

Note that while this hasn't been merged into the RFCs yet, it has already been implemented in React canaries (though you'll likely want a meta framework like Next).


refactor: replace extend util with Object.assign

Codecov Report

Merging #16 (d62ff3c) into develop (3371a2b) will increase coverage by 0.03%.
The diff coverage is 82.60%.

@@             Coverage Diff             @@
##           develop      #16      +/-   ##
===========================================
+ Coverage    73.75%   73.79%   +0.03%
===========================================
  Files           42       42
  Lines         1246     1240       -6
  Branches       329      328       -1
===========================================
- Hits           919      915       -4
+ Misses         238      237       -1
+ Partials        89       88       -1

Impacted Files
Coverage Δ

src/charts/BarChart/BarChart.ts
64.86% <ø> (ø)

src/svg/animation.ts
7.89% <0.00%> (-2.11%)
:arrow_down:

src/utils/extend.ts
100.00% <ø> (+9.09%)
:arrow_up:

src/core/data/highLow.ts
88.88% <66.66%> (ø)

src/charts/LineChart/LineChart.ts
73.03% <100.00%> (ø)

src/charts/PieChart/PieChart.ts
83.90% <100.00%> (ø)

src/core/creation.ts
91.17% <100.00%> (-0.13%)
:arrow_down:

src/core/optionsProvider.ts
33.33% <100.00%> (-2.67%)
:arrow_down:

src/svg/SvgPath.ts
97.70% <100.00%> (-0.03%)
:arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3371a2b...d62ff3c. Read the comment docs.


set to empty string displaying monetary symbol

When setting the value of an autoNumeric to '' (an empty string) the dollar symbol shows in the input box with no following numbers

Thanks - i will look at this and update when I have a little free time

@m-hume Could you try the current development branch next ; by default the wEmpty is set to focus, which means the currency sign is only displayed when you select the input, or mouseover it.
Would that satisfy your feature request?

Since no comments have been done on this PR for a few weeks, and the new emptyInputBehavior option have been upgraded, I'll close it.
Feel free to re-open it if you deem that necessary.


System.InvalidOperationException: Cannot enterFrom in state Disposed

Description
I am using Puppeteer Sharp to convert HTML to PDF.
It seems to be working overall fine BUT there are some very frequent Unobserved (TPL) task exceptions that are being caught by my global TPL exception handler.
Complete minimal example reproducing the issue
This is my conversion code:
    public static class PdfConverter
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof(PdfConverter));

        public static async Task<byte[]> Convert(string filePath)
        {
            var fetcher = new BrowserFetcher(new BrowserFetcherOptions { Platform = Platform.Win32 });
            var revisions = fetcher.LocalRevisions();
            var executablePath = fetcher.GetExecutablePath(revisions.First());

            var options = new LaunchOptions
            {
                Headless = true,
                ExecutablePath = executablePath
            };

            byte[] result = null;

            try
            {
                using (var browser = await Puppeteer.LaunchAsync(options))
                {
                    using (var page = await browser.NewPageAsync())
                    {
                        await page.GoToAsync(filePath, WaitUntilNavigation.DOMContentLoaded);
                        await page.EmulateMediaTypeAsync(PuppeteerSharp.Media.MediaType.Screen);

                        result = await page.PdfDataAsync(new PdfOptions { Width = "80mm", Height = "1050mm" });
                        await page.CloseAsync();
                    }

                    await browser.CloseAsync();
                }
            }
            catch (Exception e)
            {
                Log.Error(e.Message, e);
            }

            return result;
        }
    }

Expected behavior:
I don't expect unobserved TPL exceptions to be thrown
Actual behavior:
My global exception handler catches unobserved exceptions:
            TaskScheduler.UnobservedTaskException += (o, e) =>
            {
                Log.Error("***Incoming UnobservedTaskException from an async TPL Task:");
                if (e?.Exception != null)
                {
                    Log.Error(e.Exception);
                }
                else
                {
                    Log.Error("UnobservedTaskException was null ? (WTF!)");
                }
            };

ERROR  |2021-06-03 13:28:49,703|2       |[Program] ***Incoming UnobservedTaskException from an async TPL Task:
ERROR  |2021-06-03 13:28:49,710|2       |[Program] System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.InvalidOperationException: Cannot enterFrom in state Disposed
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.InvalidOperationException: Cannot enterFrom in state Disposed<---

Versions

Which version of PuppeteerSharp are you using?
Puppeteer: Version 4.0.0 nuget package
The first time I used the sample code form github to fetch the latest chromium for my win32 system and then I tweaked the code to discover and use it every time because I need to be able to perform such conversions offline!
Chromium: Win32-848005

Which .NET runtime and version are you targeting?
.NET 4.8

Additional Information
The exceptions are thrown at random

There were many conversations in the past around UnobservedTaskException.  The final thought was accepting that's something that might happen when you use timeout mechanisms.
I tried to remove them, but I couldn't. I would gladly review PRs improving this.


chore: Updated deps

Codecov Report
All modified and coverable lines are covered by tests :white_check_mark:

Comparison is base (4e2aac1) 86.23% compared to head (63317a8) 80.91%.
Report is 12 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #219      +/-   ##
==========================================
- Coverage   86.23%   80.91%   -5.33%
==========================================
  Files         688      235     -453
  Lines        9750     5448    -4302
  Branches      858      844      -14
==========================================
- Hits         8408     4408    -4000
+ Misses        624      267     -357
- Partials      718      773      +55

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.


Extend saved searches to beyond reports

Currently, all kinds of searches can be saved, but only searches for reports are working correctly.
Saved searches should be able to handle all kind of data.

addressed with the addition of saved search