Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libpromise.js
6170 views
1
/**
2
* @license
3
* Copyright 2023 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
addToLibrary({
8
$promiseMap__deps: ['$HandleAllocator'],
9
$promiseMap: "new HandleAllocator();",
10
11
$getPromise__deps: ['$promiseMap'],
12
$getPromise: (id) => promiseMap.get(id).promise,
13
14
$makePromise__deps: ['$promiseMap'],
15
$makePromise: () => {
16
var promiseInfo = {};
17
promiseInfo.promise = new Promise((resolve, reject) => {
18
promiseInfo.reject = reject;
19
promiseInfo.resolve = resolve;
20
});
21
promiseInfo.id = promiseMap.allocate(promiseInfo);
22
#if RUNTIME_DEBUG
23
dbg(`makePromise: ${promiseInfo.id}`);
24
#endif
25
return promiseInfo;
26
},
27
28
$idsToPromises__deps: ['$getPromise'],
29
$idsToPromises: (idBuf, size) => {
30
var promises = [];
31
for (var i = 0; i < size; i++) {
32
var id = {{{ makeGetValue('idBuf', `i*${POINTER_SIZE}`, 'i32') }}};
33
promises[i] = getPromise(id);
34
}
35
return promises;
36
},
37
38
emscripten_promise_create__deps: ['$makePromise'],
39
emscripten_promise_create: () => makePromise().id,
40
41
emscripten_promise_destroy__deps: ['$promiseMap'],
42
emscripten_promise_destroy: (id) => {
43
#if RUNTIME_DEBUG
44
dbg(`emscripten_promise_destroy: ${id}`);
45
#endif
46
promiseMap.free(id);
47
},
48
49
emscripten_promise_resolve__deps: ['$promiseMap',
50
'$getPromise',
51
'emscripten_promise_destroy'],
52
emscripten_promise_resolve: (id, result, value) => {
53
#if RUNTIME_DEBUG
54
dbg(`emscripten_promise_resolve: ${id}`);
55
#endif
56
var info = promiseMap.get(id);
57
switch (result) {
58
case {{{ cDefs.EM_PROMISE_FULFILL }}}:
59
info.resolve(value);
60
return;
61
case {{{ cDefs.EM_PROMISE_MATCH }}}:
62
info.resolve(getPromise(value));
63
return;
64
case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}:
65
info.resolve(getPromise(value));
66
_emscripten_promise_destroy(value);
67
return;
68
case {{{ cDefs.EM_PROMISE_REJECT }}}:
69
info.reject(value);
70
return;
71
}
72
#if ASSERTIONS
73
abort("unexpected promise callback result " + result);
74
#endif
75
},
76
77
$makePromiseCallback__deps: ['$getPromise',
78
'$POINTER_SIZE',
79
'emscripten_promise_destroy',
80
'$stackAlloc',
81
'$stackRestore',
82
'$stackSave'],
83
$makePromiseCallback: (callback, userData) => {
84
return (value) => {
85
#if RUNTIME_DEBUG
86
dbg(`emscripten promise callback: ${value}`);
87
#endif
88
{{{ runtimeKeepalivePop() }}};
89
var stack = stackSave();
90
// Allocate space for the result value and initialize it to NULL.
91
var resultPtr = stackAlloc(POINTER_SIZE);
92
{{{ makeSetValue('resultPtr', 0, '0', '*') }}};
93
try {
94
var result =
95
{{{ makeDynCall('ippp', 'callback') }}}(resultPtr, userData, value);
96
var resultVal = {{{ makeGetValue('resultPtr', 0, '*') }}};
97
} catch (e) {
98
// If the thrown value is potentially a valid pointer, use it as the
99
// rejection reason. Otherwise use a null pointer as the reason. If we
100
// allow arbitrary objects to be thrown here, we will get a TypeError in
101
// MEMORY64 mode when they are later converted to void* rejection
102
// values.
103
#if MEMORY64
104
if (typeof e != 'bigint') {
105
throw 0n;
106
}
107
#else
108
if (typeof e != 'number') {
109
throw 0;
110
}
111
#endif
112
throw e;
113
} finally {
114
// Thrown errors will reject the promise, but at least we will restore
115
// the stack first.
116
stackRestore(stack);
117
}
118
switch (result) {
119
case {{{ cDefs.EM_PROMISE_FULFILL }}}:
120
return resultVal;
121
case {{{ cDefs.EM_PROMISE_MATCH }}}:
122
return getPromise(resultVal);
123
case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}:
124
var ret = getPromise(resultVal);
125
_emscripten_promise_destroy(resultVal);
126
return ret;
127
case {{{ cDefs.EM_PROMISE_REJECT }}}:
128
throw resultVal;
129
}
130
#if ASSERTIONS
131
abort("unexpected promise callback result " + result);
132
#endif
133
};
134
},
135
136
emscripten_promise_then__deps: ['$promiseMap',
137
'$getPromise',
138
'$makePromiseCallback'],
139
emscripten_promise_then: (id, onFulfilled, onRejected, userData) => {
140
#if RUNTIME_DEBUG
141
dbg(`emscripten_promise_then: ${id}`);
142
#endif
143
{{{ runtimeKeepalivePush() }}};
144
var promise = getPromise(id);
145
var newId = promiseMap.allocate({
146
promise: promise.then(makePromiseCallback(onFulfilled, userData),
147
makePromiseCallback(onRejected, userData))
148
});
149
#if RUNTIME_DEBUG
150
dbg(`emscripten_promise_then: -> ${newId}`);
151
#endif
152
return newId;
153
},
154
155
emscripten_promise_all__deps: ['$promiseMap', '$idsToPromises'],
156
emscripten_promise_all: (idBuf, resultBuf, size) => {
157
var promises = idsToPromises(idBuf, size);
158
#if RUNTIME_DEBUG
159
dbg(`emscripten_promise_all: ${promises}`);
160
#endif
161
var id = promiseMap.allocate({
162
promise: Promise.all(promises).then((results) => {
163
if (resultBuf) {
164
for (var i = 0; i < size; i++) {
165
var result = results[i];
166
{{{ makeSetValue('resultBuf', `i*${POINTER_SIZE}`, 'result', '*') }}};
167
}
168
}
169
return resultBuf;
170
})
171
});
172
#if RUNTIME_DEBUG
173
dbg(`create: ${id}`);
174
#endif
175
return id;
176
},
177
178
$setPromiseResult__internal: true,
179
$setPromiseResult: (ptr, fulfill, value) => {
180
#if ASSERTIONS
181
assert(typeof value == 'undefined' || typeof value === 'number', `native promises can only handle numeric results (${value} ${typeof value})`);
182
#endif
183
var result = fulfill ? {{{ cDefs.EM_PROMISE_FULFILL }}} : {{{ cDefs.EM_PROMISE_REJECT }}}
184
{{{ makeSetValue('ptr', C_STRUCTS.em_settled_result_t.result, 'result', 'i32') }}};
185
{{{ makeSetValue('ptr', C_STRUCTS.em_settled_result_t.value, 'value', '*') }}};
186
},
187
188
emscripten_promise_all_settled__deps: ['$promiseMap', '$idsToPromises', '$setPromiseResult'],
189
emscripten_promise_all_settled: (idBuf, resultBuf, size) => {
190
var promises = idsToPromises(idBuf, size);
191
#if RUNTIME_DEBUG
192
dbg(`emscripten_promise_all_settled: ${promises}`);
193
#endif
194
var id = promiseMap.allocate({
195
promise: Promise.allSettled(promises).then((results) => {
196
if (resultBuf) {
197
var offset = resultBuf;
198
for (var i = 0; i < size; i++, offset += {{{ C_STRUCTS.em_settled_result_t.__size__ }}}) {
199
if (results[i].status === 'fulfilled') {
200
setPromiseResult(offset, true, results[i].value);
201
} else {
202
setPromiseResult(offset, false, results[i].reason);
203
}
204
}
205
}
206
return resultBuf;
207
})
208
});
209
#if RUNTIME_DEBUG
210
dbg(`create: ${id}`);
211
#endif
212
return id;
213
},
214
215
emscripten_promise_any__deps: [
216
'$promiseMap', '$idsToPromises',
217
#if !SUPPORTS_PROMISE_ANY && !INCLUDE_FULL_LIBRARY
218
() => error("emscripten_promise_any used, but Promise.any is not supported by the current runtime configuration (run with EMCC_DEBUG=1 in the env for more details)"),
219
#endif
220
],
221
emscripten_promise_any: (idBuf, errorBuf, size) => {
222
var promises = idsToPromises(idBuf, size);
223
#if RUNTIME_DEBUG
224
dbg(`emscripten_promise_any: ${promises}`);
225
#endif
226
#if ASSERTIONS
227
assert(typeof Promise.any != 'undefined', "Promise.any does not exist");
228
#endif
229
var id = promiseMap.allocate({
230
promise: Promise.any(promises).catch((err) => {
231
if (errorBuf) {
232
for (var i = 0; i < size; i++) {
233
{{{ makeSetValue('errorBuf', `i*${POINTER_SIZE}`, 'err.errors[i]', '*') }}};
234
}
235
}
236
throw errorBuf;
237
})
238
});
239
#if RUNTIME_DEBUG
240
dbg(`create: ${id}`);
241
#endif
242
return id;
243
},
244
245
emscripten_promise_race__deps: ['$promiseMap', '$idsToPromises'],
246
emscripten_promise_race: (idBuf, size) => {
247
var promises = idsToPromises(idBuf, size);
248
#if RUNTIME_DEBUG
249
dbg(`emscripten_promise_race: ${promises}`);
250
#endif
251
var id = promiseMap.allocate({
252
promise: Promise.race(promises)
253
});
254
#if RUNTIME_DEBUG
255
dbg(`create: ${id}`);
256
#endif
257
return id;
258
},
259
260
emscripten_promise_await__async: 'auto',
261
#if ASYNCIFY
262
emscripten_promise_await__deps: ['$getPromise', '$setPromiseResult'],
263
#endif
264
emscripten_promise_await: (returnValuePtr, id) => {
265
#if ASYNCIFY
266
#if RUNTIME_DEBUG
267
dbg(`emscripten_promise_await: ${id}`);
268
#endif
269
return getPromise(id).then(
270
value => setPromiseResult(returnValuePtr, true, value),
271
error => setPromiseResult(returnValuePtr, false, error)
272
);
273
#else
274
abort('emscripten_promise_await is only available with ASYNCIFY');
275
#endif
276
},
277
});
278
279