kopia lustrzana https://github.com/micropython/micropython
webassembly/asyncio: Add simple asyncio module with create_task, sleep.
Signed-off-by: Damien George <damien@micropython.org>pull/14193/head
rodzic
5114f2c1ea
commit
7ebf3afd3e
|
@ -0,0 +1,24 @@
|
|||
# Minimal asyncio interface that uses JavaScript for scheduling.
|
||||
# Only implements a very small subset of the asyncio API.
|
||||
|
||||
import jsffi
|
||||
|
||||
|
||||
class Task:
|
||||
def __init__(self, coro):
|
||||
self._coro = coro
|
||||
|
||||
def done(self):
|
||||
return self._coro is None
|
||||
|
||||
|
||||
async def sleep(s):
|
||||
await jsffi.async_timeout(s)
|
||||
|
||||
|
||||
def create_task(coro):
|
||||
if not hasattr(coro, "send"):
|
||||
raise TypeError("coroutine expected")
|
||||
t = Task(coro)
|
||||
jsffi.create_task(t)
|
||||
return t
|
|
@ -61,12 +61,47 @@ static mp_obj_t mp_jsffi_to_js(mp_obj_t arg) {
|
|||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_to_js_obj, mp_jsffi_to_js);
|
||||
|
||||
// *FORMAT-OFF*
|
||||
EM_JS(void, create_task, (uint32_t * out), {
|
||||
const task = proxy_convert_mp_to_js_obj_jsside(out);
|
||||
queueMicrotask(() =>
|
||||
Promise.resolve(task._coro).then(() => {
|
||||
task._coro = null;
|
||||
})
|
||||
);
|
||||
});
|
||||
// *FORMAT-ON*
|
||||
|
||||
static mp_obj_t mp_jsffi_create_task(mp_obj_t arg) {
|
||||
uint32_t out[3];
|
||||
proxy_convert_mp_to_js_obj_cside(arg, out);
|
||||
create_task(out);
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_create_task_obj, mp_jsffi_create_task);
|
||||
|
||||
// *FORMAT-OFF*
|
||||
EM_JS(void, promise_with_timeout_ms, (double ms, uint32_t * out), {
|
||||
const ret = new Promise((resolve) => setTimeout(resolve, ms));
|
||||
proxy_convert_js_to_mp_obj_jsside(ret, out);
|
||||
});
|
||||
// *FORMAT-ON*
|
||||
|
||||
static mp_obj_t mp_jsffi_async_timeout(mp_obj_t arg) {
|
||||
uint32_t out[3];
|
||||
promise_with_timeout_ms(mp_obj_get_float_to_d(arg) * 1000.0, out);
|
||||
return proxy_convert_js_to_mp_obj_cside(out);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(mp_jsffi_async_timeout_obj, mp_jsffi_async_timeout);
|
||||
|
||||
static const mp_rom_map_elem_t mp_module_jsffi_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_jsffi) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_JsProxy), MP_ROM_PTR(&mp_type_jsproxy) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_create_proxy), MP_ROM_PTR(&mp_jsffi_create_proxy_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_to_js), MP_ROM_PTR(&mp_jsffi_to_js_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_create_task), MP_ROM_PTR(&mp_jsffi_create_task_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_async_timeout), MP_ROM_PTR(&mp_jsffi_async_timeout_obj) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(mp_module_jsffi_globals, mp_module_jsffi_globals_table);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
module("asyncio.py", base_path="$(PORT_DIR)")
|
||||
|
||||
require("abc")
|
||||
require("base64")
|
||||
require("collections")
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Test asyncio.create_task().
|
||||
|
||||
const mp = await (await import(process.argv[2])).loadMicroPython();
|
||||
|
||||
globalThis.p0 = new Promise((resolve, reject) => {
|
||||
resolve(123);
|
||||
});
|
||||
|
||||
globalThis.p1 = new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
console.log("setTimeout resolved");
|
||||
resolve(456);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
mp.runPython(`
|
||||
import js
|
||||
import asyncio
|
||||
|
||||
async def task(id, pp):
|
||||
print("task start", id)
|
||||
print("task await", id, await pp)
|
||||
print("task await", id, await pp)
|
||||
print("task end", id)
|
||||
|
||||
print("start")
|
||||
t1 = asyncio.create_task(task(1, js.p0))
|
||||
t2 = asyncio.create_task(task(2, js.p1))
|
||||
print("t1", t1.done(), t2.done())
|
||||
print("end")
|
||||
`);
|
||||
|
||||
await globalThis.p1;
|
||||
await globalThis.p1;
|
||||
await globalThis.p1;
|
||||
await globalThis.p1;
|
||||
|
||||
mp.runPython(`
|
||||
print("restart")
|
||||
print("t1", t1.done(), t2.done())
|
||||
`);
|
|
@ -0,0 +1,14 @@
|
|||
start
|
||||
t1 False False
|
||||
end
|
||||
task start 1
|
||||
task start 2
|
||||
task await 1 123
|
||||
task await 1 123
|
||||
task end 1
|
||||
setTimeout resolved
|
||||
task await 2 456
|
||||
task await 2 456
|
||||
task end 2
|
||||
restart
|
||||
t1 True True
|
|
@ -0,0 +1,25 @@
|
|||
// Test asyncio.sleep().
|
||||
|
||||
const mp = await (await import(process.argv[2])).loadMicroPython();
|
||||
|
||||
await mp.runPythonAsync(`
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
print("main start")
|
||||
t0 = time.time()
|
||||
await asyncio.sleep(0.25)
|
||||
dt = time.time() - t0
|
||||
print(0.2 <= dt <= 0.3)
|
||||
|
||||
async def task():
|
||||
print("task start")
|
||||
t0 = time.time()
|
||||
await asyncio.sleep(0.25)
|
||||
dt = time.time() - t0
|
||||
print(0.2 <= dt <= 0.3)
|
||||
print("task end")
|
||||
|
||||
asyncio.create_task(task())
|
||||
print("main end")
|
||||
`);
|
|
@ -0,0 +1,6 @@
|
|||
main start
|
||||
True
|
||||
main end
|
||||
task start
|
||||
True
|
||||
task end
|
|
@ -681,6 +681,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
|||
elif args.target == "webassembly":
|
||||
skip_tests.add("basics/string_format_modulo.py") # can't print nulls to stdout
|
||||
skip_tests.add("basics/string_strip.py") # can't print nulls to stdout
|
||||
skip_tests.update(t for t in tests if t.startswith("extmod/asyncio_"))
|
||||
skip_tests.add("extmod/binascii_a2b_base64.py")
|
||||
skip_tests.add("extmod/re_stack_overflow.py")
|
||||
skip_tests.add("extmod/time_res.py")
|
||||
|
|
Ładowanie…
Reference in New Issue