From b1161707354090323f6f3d1cd8af19c0fa314aaa Mon Sep 17 00:00:00 2001 From: playX Date: Fri, 10 Dec 2021 15:54:20 +0300 Subject: [PATCH] js: add support for Promise.wait() (#12781) * builtin/js: Change Promise to Promise * js: codegen support for Promise.wait() * checker: checker support for Promise.wait() --- vlib/builtin/js/promise.js.v | 30 +++++++++++++-------------- vlib/v/checker/checker.v | 10 +++++++++ vlib/v/gen/js/fn.v | 40 ++++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/vlib/builtin/js/promise.js.v b/vlib/builtin/js/promise.js.v index 35415b6c04..a1a80626d0 100644 --- a/vlib/builtin/js/promise.js.v +++ b/vlib/builtin/js/promise.js.v @@ -14,46 +14,46 @@ pub fn JS.Promise.race(JS.Array) JS.Promise // The Promise object represents the eventual completion (or failure) // of an asynchronous operation and its resulting value. -pub struct Promise { +pub struct Promise { mut: promise JS.Promise [noinit] } -pub fn promise_new(executor fn (resolve fn (T), reject fn (E))) Promise { +pub fn promise_new(executor fn (resolve fn (T), reject fn (JS.Any))) Promise { promise := JS.Promise.prototype.constructor(executor) - return Promise{promise} + return Promise{promise} } -pub fn (p Promise) then(on_fullfilled fn (T), on_rejected fn (E)) { +pub fn (p Promise) then(on_fullfilled fn (T), on_rejected fn (JS.Any)) { p.promise.then(on_fullfilled, on_rejected) } // catch method returns a Promise and deals with rejected cases only. -pub fn (p Promise) catch(callback fn (error JS.Any)) Promise { +pub fn (p Promise) catch(callback fn (error JS.Any)) Promise { promise := p.promise.catch(callback) - return Promise{promise} + return Promise{promise} } -pub fn (p Promise) finally(callback fn ()) Promise { +pub fn (p Promise) finally(callback fn ()) Promise { promise := p.promise.finally(callback) - return Promise{promise} + return Promise{promise} } // reject returns promise which was rejected because of specified error -pub fn promise_reject(error E) Promise { +pub fn promise_reject(error JS.Any) Promise { promise := JS.Promise.reject(error) - return Promise{promise} + return Promise{promise} } // resolve returns promise which was resolved with specified value -pub fn promise_resolve(result T) Promise { - promise := JS.Promise.resolve(error) - return Promise{promise} +pub fn promise_resolve(result T) Promise { + promise := JS.Promise.resolve(result) + return Promise{promise} } // race returns returns a promise that fulfills or rejects as soon as one of // the promises in an iterable fulfills or rejects, with the value or reason from that promise. -pub fn promise_race(promises []Promise) Promise { +pub fn promise_race(promises []Promise) Promise { promises_ := JS.Array.prototype.constructor() for elem in promises { @@ -61,7 +61,7 @@ pub fn promise_race(promises []Promise) Promise { } promise := JS.Promise.race(promises_) - return Promise{promise} + return Promise{promise} } pub fn JS.Promise.all(JS.Array) JS.Promise diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e695e88e78..7cf904cccf 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2044,6 +2044,16 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { node.return_type = final_left_sym.info.elem_type return node.return_type } + } else if c.pref.backend.is_js() && left_sym.name.starts_with('Promise<') + && method_name == 'wait' { + info := left_sym.info as ast.Struct + if node.args.len > 0 { + c.error('wait() does not have any arguments', node.args[0].pos) + } + c.table.cur_fn.has_await = true + node.return_type = info.concrete_types[0] + node.return_type.set_flag(.optional) + return node.return_type } else if left_sym.kind == .thread && method_name == 'wait' { info := left_sym.info as ast.Thread if node.args.len > 0 { diff --git a/vlib/v/gen/js/fn.v b/vlib/v/gen/js/fn.v index 084bc0b522..ed5c81dea8 100644 --- a/vlib/v/gen/js/fn.v +++ b/vlib/v/gen/js/fn.v @@ -190,9 +190,15 @@ fn (mut g JsGen) method_call(node ast.CallExpr) { g.gen_expr_to_string(node.left, node.left_type) return } + is_async := node.name == 'wait' + && g.table.get_type_symbol(node.receiver_type).name.starts_with('Promise<') call_return_is_optional := it.return_type.has_flag(.optional) if call_return_is_optional { - g.writeln('(function(){') + if is_async { + g.writeln('(async function (){') + } else { + g.writeln('(function(){') + } g.inc_indent() g.writeln('try {') g.inc_indent() @@ -308,20 +314,27 @@ fn (mut g JsGen) method_call(node ast.CallExpr) { receiver_type_name = 'array' } } - mut name := util.no_dots('${receiver_type_name}_$node.name') - // name = g.generic_fn_name(node.concrete_types, name, false) - g.write('${name}(') - g.expr(it.left) - g.gen_deref_ptr(it.left_type) - g.write(',') - for i, arg in it.args { - g.expr(arg.expr) - if i != it.args.len - 1 { - g.write(', ') + if is_async { + g.write('await ') + g.expr(it.left) + g.write('.promise') + } else { + mut name := util.no_dots('${receiver_type_name}_$node.name') + + name = g.generic_fn_name(node.concrete_types, name, false) + g.write('${name}(') + g.expr(it.left) + g.gen_deref_ptr(it.left_type) + g.write(',') + for i, arg in it.args { + g.expr(arg.expr) + if i != it.args.len - 1 { + g.write(', ') + } } + g.write(')') } - g.write(')') if call_return_is_optional { // end unwrap @@ -406,6 +419,7 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { g.write(')') return } + name = g.generic_fn_name(node.concrete_types, name, false) g.expr(it.left) g.write('${name}(') @@ -589,7 +603,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { } name = g.js_name(name) - // name = g.generic_fn_name(g.table.cur_concrete_types, name, true) + name = g.generic_fn_name(g.cur_concrete_types, name, true) if name in parser.builtin_functions { name = 'builtin__$name' }