Skip to content

Commit f43951f

Browse files
committed
DeferredPromise type wrapper
1 parent 7655b90 commit f43951f

File tree

5 files changed

+82
-1
lines changed

5 files changed

+82
-1
lines changed

src/workerd/jsg/deferred-promise.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,38 @@ inline DeferredPromiseResolverPair<T> newDeferredPromiseAndResolver() {
18881888
.resolver = DeferredPromiseResolver<T>(kj::mv(stateRef))};
18891889
}
18901890

1891+
template <typename TypeWrapper>
1892+
class DeferredPromiseWrapper {
1893+
public:
1894+
template <typename T>
1895+
static constexpr const char* getName(DeferredPromise<T>*) {
1896+
return "Promise";
1897+
}
1898+
1899+
template <typename T>
1900+
v8::Local<v8::Promise> wrap(jsg::Lock& js,
1901+
v8::Local<v8::Context> context,
1902+
kj::Maybe<v8::Local<v8::Object>> creator,
1903+
DeferredPromise<T>&& promise) {
1904+
auto& wrapper = *static_cast<TypeWrapper*>(this);
1905+
return wrapper.wrap(js, context, creator, promise.toJsPromise(js));
1906+
}
1907+
1908+
template <typename T>
1909+
kj::Maybe<DeferredPromise<T>> tryUnwrap(Lock& js,
1910+
v8::Local<v8::Context> context,
1911+
v8::Local<v8::Value> handle,
1912+
DeferredPromise<T>*,
1913+
kj::Maybe<v8::Local<v8::Object>> parentObject) {
1914+
auto& wrapper = *static_cast<TypeWrapper*>(this);
1915+
KJ_IF_SOME(jsPromise,
1916+
wrapper.tryUnwrap(js, context, handle, static_cast<Promise<T>*>(nullptr), parentObject)) {
1917+
return DeferredPromise<T>::fromJsPromise(js, kj::mv(jsPromise));
1918+
}
1919+
return kj::none;
1920+
}
1921+
};
1922+
18911923
// ======================================================================================
18921924
// When NOT To Use DeferredPromise (Even For Pure C++ Code)
18931925
// ======================================================================================

src/workerd/jsg/promise-test.c++

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace {
1010
V8System v8System;
1111

1212
int promiseTestResult = 0;
13+
int deferredPromiseTestResult = 0;
1314
kj::String catchTestResult;
1415

1516
struct PromiseContext: public jsg::Object, public jsg::ContextGlobal {
@@ -98,6 +99,27 @@ struct PromiseContext: public jsg::Object, public jsg::ContextGlobal {
9899
return result;
99100
}
100101

102+
DeferredPromise<kj::String> makeDeferredPromise(jsg::Lock& js) {
103+
auto [p, r] = js.newDeferredPromiseAndResolver<int>();
104+
deferredResolver = kj::mv(r);
105+
return p.then(js, [](jsg::Lock&, int i) { return i * 2; })
106+
.then(js, [](jsg::Lock& js, int i) {
107+
return jsg::DeferredPromise<int>::resolved(i + 2);
108+
}).then(js, [](jsg::Lock& js, int i) { return kj::str(i); });
109+
}
110+
111+
void deferredResolvePromise(Lock& js, int i) {
112+
KJ_ASSERT_NONNULL(deferredResolver).resolve(js, kj::mv(i));
113+
}
114+
115+
void setDeferredResult(jsg::Lock& js, DeferredPromise<kj::String> promise) {
116+
// Throwing away the result of `.then()` doesn't cancel it!
117+
promise
118+
.then(js, [](jsg::Lock&, kj::String str) {
119+
deferredPromiseTestResult = str.parseAs<int>();
120+
}).then(js, [](jsg::Lock&) { deferredPromiseTestResult += 60000; });
121+
}
122+
101123
JSG_RESOURCE_TYPE(PromiseContext) {
102124
JSG_READONLY_PROTOTYPE_PROPERTY(promise, makePromise);
103125
JSG_METHOD(resolvePromise);
@@ -111,9 +133,14 @@ struct PromiseContext: public jsg::Object, public jsg::ContextGlobal {
111133
JSG_METHOD(whenResolved);
112134

113135
JSG_METHOD(thenable);
136+
137+
JSG_READONLY_PROTOTYPE_PROPERTY(deferredPromise, makeDeferredPromise);
138+
JSG_METHOD(deferredResolvePromise);
139+
JSG_METHOD(setDeferredResult);
114140
}
115141

116142
kj::Maybe<Promise<int>::Resolver> resolver;
143+
kj::Maybe<DeferredPromise<int>::Resolver> deferredResolver;
117144
};
118145
JSG_DECLARE_ISOLATE_TYPE(PromiseIsolate, PromiseContext);
119146

@@ -192,5 +219,19 @@ KJ_TEST("thenable") {
192219
e.expectEval("thenable({ then(res) { res(123) } })", "number", "123");
193220
}
194221

222+
KJ_TEST("jsg::DeferredPromise<T>") {
223+
Evaluator<PromiseContext, PromiseIsolate> e(v8System);
224+
225+
e.expectEval("setDeferredResult(deferredPromise.then(i => i + 1 /* oops, i is a string */));\n"
226+
"deferredResolvePromise(123)",
227+
"undefined", "undefined");
228+
229+
KJ_EXPECT(deferredPromiseTestResult == 0);
230+
231+
e.runMicrotasks();
232+
233+
KJ_EXPECT(deferredPromiseTestResult == 62481);
234+
}
235+
195236
} // namespace
196237
} // namespace workerd::jsg::test

src/workerd/jsg/rtti.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,13 @@ struct BuildRtti<Configuration, jsg::Promise<T>> {
450450
}
451451
};
452452

453+
template <typename Configuration, typename T>
454+
struct BuildRtti<Configuration, jsg::DeferredPromise<T>> {
455+
static void build(Type::Builder builder, Builder<Configuration>& rtti) {
456+
BuildRtti<Configuration, T>::build(builder.initPromise().initValue(), rtti);
457+
}
458+
};
459+
453460
template <typename Configuration>
454461
struct BuildRtti<Configuration, v8::Promise> {
455462
static void build(Type::Builder builder, Builder<Configuration>& rtti) {

src/workerd/jsg/type-wrapper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ class TypeWrapper: public DynamicResourceTypeMap<Self>,
401401
public BufferSourceWrapper,
402402
public FunctionWrapper<Self>,
403403
public PromiseWrapper<Self>,
404+
public DeferredPromiseWrapper<Self>,
404405
public NonCoercibleWrapper<Self>,
405406
public MemoizedIdentityWrapper<Self>,
406407
public IdentifiedWrapper<Self>,
@@ -464,6 +465,7 @@ class TypeWrapper: public DynamicResourceTypeMap<Self>,
464465
USING_WRAPPER(BufferSourceWrapper);
465466
USING_WRAPPER(FunctionWrapper<Self>);
466467
USING_WRAPPER(PromiseWrapper<Self>);
468+
USING_WRAPPER(DeferredPromiseWrapper<Self>);
467469
USING_WRAPPER(NonCoercibleWrapper<Self>);
468470
USING_WRAPPER(MemoizedIdentityWrapper<Self>);
469471
USING_WRAPPER(IdentifiedWrapper<Self>);

src/workerd/tests/bench-deferred-promise.c++

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
// 3. Pending with continuations - Setup overhead comparison
1515
// 4. Conversion to JS - Cost when you do need a V8 promise
1616

17-
#include <workerd/jsg/deferred-promise.h>
1817
#include <workerd/jsg/jsg.h>
1918
#include <workerd/jsg/setup.h>
2019
#include <workerd/tests/bench-tools.h>

0 commit comments

Comments
 (0)