How do I build an API that lets me do `await t.a().b()` & `await t.a(); await t.b()`?
I want to build an API that lets me chain in two ways:
This is known as the builder pattern. Usually it's straightforward, but I'm not sure how to create one when promises are involved.
NOTE: This is the way https://testcafe.io/ 's api works. I'm trying to build a similar api. See https://discord.com/channels/436251713830125568/1158962809401516062/1159217101471481896 (links to a few messages down)
21 Replies
You are wanting to call
a()
and have it return a Promise
or an object with a b
function deepending on how it was called. (The "dot" (.
) operator is higher precedence than await
, so await a().b().c() === await (a().b().c())
) .
There is no way within a function call to know whether the returning context is expecting a Promise
or not, so you can't do what you're describing.
I mean you could return a Promise
from a()
that has a b
method, but you need to wait for a()
to resolve before you'd have a value to do anything with in b
, and you're not waiting in a().b().c()
.
(Note that the then()
method of a Promise
is called with the result of the Promise
& accepts a Promise
as output, but that's a special case.)
Are you talking about the two code examples on https://testcafe.io? They're two different frameworks: Testcase & Selenium, not different examples of a single framework.
If you're not taking about those pieces of code, where are you seeing these alternate syntaxes?@dys 🐙 I'm only referring to the left hand side. It doesn't show it being called like in my second example, but I know it works both ways because I've got many examples in my project EDIT: Oops. I was wrong. I've updated my OP
There is no way within a function call to know whether the returning context is expecting a Promise or not, so you can't do what you're describing.I believe the library returns a promise that's been assigned other functions to make it so the code doesn't know how its being called; the single return can handle both scenarios
Dysbulic is correct, what you're describing isn't possible.
a
can either return a promise or an object with other methods, not both. If it returns a promise, chaining won't work without awaiting the promise first. If it returns an object, awaiting it in the second example will do nothingYou could maybe do it if the
b()
and c()
methods internally call this.then()
or await this
. But I'm not sure if it would actually work.I don't think you can know whether a method is being called with
await
in the method itself thoughYou don't need to
You can call
.then()
on a promise that has already been fulfilled
So you just do it every time the function is called and use the resulting promise/value in the body of the methodit feels like a terrible idea overall though, to have a method that returns Schrodinger's Promise
What do you mean by "Schrodinger's Promise" ?
A promise that is maybe already fulfilled?
effectively yeah
'Cause that's what promises are for, doing stuff after some other stuff has finished running.
regardless, it makes the await pointless, right? Cause you can call it or not call it (on
a()
and b()
), and the result is the sameYes
But maybe the results of a() has other stuff they are interested in
And actually the first .b() and the second aren't even called on the same object. One's on the promise the other on the value inside the promise so you don't even need to have the same implementation in both.
They just need to have the same signature for it to work
(
a === await a // false
)
I'll try something when I get homea
can either return a promise or an object with other methods, not both
The above runs. Is this different from what you mean?hm, I guess not. But that feels unbelievably cursed
agreed. I didn't know it was possible until I tried to copy this API
but the API is really great, even if the impl is weird
actually let me test something
okay I've been mistaken
I guess I've never tried the above, I only thought I had. But this works:
I suppose that's much easier to implement
updated my OP. Still not sure how to do this, either
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
(don't ask me what it does or how it does it... I don't understand it either
as for https://discord.com/channels/436251713830125568/1158962809401516062/1159219191400890428
you have the have your
t
, t.a()
and t.b()
either be the same object (by doing return this
in every method) or implement the same interface (basically same as before but the object that gets returned is not the same as the one that was called upon)TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
@Rägnar O'ock thanks! I'll check these out soon
I am aware... but there's no point in hiding those links