google / blockly-samples

Plugins, codelabs, and examples related to the Blockly library.
http://github.com/google/blockly
Apache License 2.0
850 stars 625 forks source link

feature async / await support (I'd like to implement, will it be supported?) #1356

Open jogibear9988 opened 5 years ago

jogibear9988 commented 5 years ago

I'd like to introduce blocks wich return promises and should be awaited.

For this, in Codegeneration every function should be prefixed by an "async " so promises could be awaited inside it. And also Procedure or function calls should be awaited.

I'd like to introduce a setting with async support, so it could be used.

Will a pull req. for this be accepted?

RoboErikG commented 5 years ago

This is already possible using Neil's JS-Interpreter. There shouldn't need to be any specific support for it in Blockly since you can generate async code just as easily as synchronous code.

I don't think having a global switch in the generator to mark everything async makes sense, since many functions don't have a return and have no reason to run async. Let me know if I'm missing something.

jogibear9988 commented 5 years ago

Surely, with the interpreter it would work, but the sandbox of the interpreter consumes performance and memory.

I‘d like to add a Property to async commands, so the outside function can look if any inside block needs an await, and so I will create the async outside around. (hopefully this is possible)

jogibear9988 commented 5 years ago

@RoboErikG can we reopen?

RoboErikG commented 5 years ago

Hmm...I can see the use for it but I'm not sure we'd want to support it in core Blockly. Are you able to share a link to a project where you're using this feature so we could look at it and discuss internally?

jogibear9988 commented 5 years ago

I've not yet implemented it, I wanted to know if it will be supported. Problem is, I would not like to copy most of the javascript generation code, only to add this feature, and need to maintain it as a different fork.

jogibear9988 commented 5 years ago

Not will, if it has the chance to be supported.

jogibear9988 commented 5 years ago

for example, see here my block i did for ioBroker:

2019-01-25 22 28 32

see the wait block (warten), if it is done without async/await the blockly would get really complex

jogibear9988 commented 5 years ago

also much more code is returning nowdays, so I think it would create a much greater use base

RoboErikG commented 5 years ago

If there's a minimal change to generators that could be made to make this an option we'd consider supporting it.

There's a lot of corner cases that would need to be considered, though, and it's not clear what an end user's expectation would be if, for example, you did the following:

set 'foo' to 5; call myFunc() if foo = 5 print "this"; else print "that";

define myFunc wait 1 second set foo to 10

Should myFunc be async? Would users have any idea when something is async vs not async? Do you need UI to show it? How do you distinguish that from cases where the user wants to pause execution for a bit?

AnmAtAnm commented 5 years ago

Currently, all of the blocks we ship with Blockly work in all five languages. Continuations are not available in ES5, PHP, Python, Lua, or Dart 1 (that I can find), and Continuation Passing Style (the functional programming equivalent) would produce code that is strange to a lot of users.

We probably need to have a discussion before committing to support a feature for only one or two languages.

jogibear9988 commented 5 years ago

I think blockly is mostly used for javascript (where continuations exist is ES6, and current browsers all support them). and it should be optional, i will look, maybe changes are only needed in codegen, i want optional async support so for example not experienced users could create a sleep in a loop without defining a sub function. if someone would not define async block, nothing should be changed.

Python: https://docs.python.org/3/library/asyncio-task.html Dart: https://www.dartlang.org/articles/language/await-async#await-expressions

AnmAtAnm commented 5 years ago

Blockly is used by a large number of apps that don't generate JavaScript. Scratch, MakeCode, BlockDuino are all significant examples.

Blockly intentionally supports older browsers because schools, libraries, and developing nations often do not have the resources to maintain systems with the latest updates (if they have technical staff, at all).

And you're right... Python should have been included in the languages that support it. 3/5 is getting close to a solid case to support it.

I would want to see it optional, off by default, and easy to test in tests/playground.html. For each of JavaScript, Python, and Dart.

It is probably easiest to start by writing a demo that just overrides the JavaScript generator for functions and function calls, and proves the utility of the feature.

jogibear9988 commented 5 years ago

yes, but as I said, I'd like that the support is optional. So noone has to use it, but if it's needed it's there...

claytongulick commented 4 years ago

I'd like to jump into this also with a thumbs up, +1, etc...

For my use case, I want to use blockly as a low-code environment for semi-technical staff to generate flows in a designer environment.

The generated script will be run in Node.

Part of the core API that will be exposed to the blockly environment will be the ability to load/save values (from a mongo document - all async), call certain external API's (all async), and to issue notifications that need to await a human response. Having a clean way to execute async blocks is pretty critical to this use case.

I love the project! For now, since this is such a core requirement, I'll have to do a fork to support async blocks, but would love to issue a PR and/or collaborate on how this could be added to core.

Thanks!!

seldomU commented 4 years ago

We are using JavaScript async/await and I think we only made one change near Blockly's core for it: add the async keyword to the _proceduredef(no)return generator and the await keyword to _procedurecall(no)return. The remaining changes are in our own blocks: (1) All blocks that generate a function definition have to add the async keyword. That's typically the program's main function and a bunch of event handlers. (2) Blocks that represent promises need to add the await keyword.

This way we can create async programs that mix promise blocks with Blockly's standard synchronous blocks. You can see an example application here. All blocks are synchronous except for the input reading and wait blocks. There is a tab on the right side that lets you inspect the generated code.

We're also generating async Lua with the help of coroutines. You can see that here.

@RoboErikG regarding your code example. I think those questions have to be answered per application, not Blockly-wide. For non programmer users, my answers would be: everything is considered async and the caller always waits for the promise to finish.

RestlessRabbits commented 4 years ago

@seldomU : Would you mind going a bit further into detail about your changes and files?

I try to chain blocks that handle requests and deliver a promise/value (when resolved) and I would like to avoid using Neil's JS-Interpreter if possible.

That's why I tried to apply the changes you described, but I still run into the error Invalid code generated: [object Promise] and I'm a bit puzzled at the moment.

Thank you very much in advance!

seldomU commented 4 years ago

That sounds like your block's code generator returns an actual promise instead of a code snippet. Just to clarify, the code generation itself stays synchronous - async/await have to be added to the generated code. Here's an example code generator for awaiting a promise:

Blockly.JavaScript['wait_a_minute'] = function(block){
    return "await new Promise( resolve => setTimeout(resolve, 60000))\n;";
}

I hope that answers your question.

yar2001 commented 4 years ago

I place the following code in the custom js and all functions will become async/await successfully. Just copy it.

Blockly.JavaScript['procedures_callreturn'] = function(block) {
  // Call a procedure with a return value.
  var funcName = Blockly.JavaScript.variableDB_.getName(
      block.getFieldValue('NAME'), Blockly.PROCEDURE_CATEGORY_NAME);
  var args = [];
  var variables = block.getVars();
  for (var i = 0; i < variables.length; i++) {
    args[i] = Blockly.JavaScript.valueToCode(block, 'ARG' + i,
        Blockly.JavaScript.ORDER_NONE) || 'null';
  }
  var code = 'await ' + funcName + '(' + args.join(', ') + ')';
  return [code, Blockly.JavaScript.ORDER_AWAIT];
};

// ...
// when ask code
let code = Blockly['JavaScript'].workspaceToCode(workspace).replace(/(?<=^|\n)function \w+\(.*\)/g, 'async $&');
moniika commented 4 years ago

I place the following code in the custom js and all functions will become async /await successfully. Just copy it.

Blockly.JavaScript.procedures_callreturn = function(a) {
    for (var b = Blockly.JavaScript.variableDB_.getName(a.getFieldValue("NAME"), Blockly.JavaScript.PROCEDURE_CATEGORY_NAME), c = [], d = a.getVars(), e = 0; e < d.length; e++)
        c[e] = Blockly.JavaScript.valueToCode(a, "ARG" + e, Blockly.JavaScript.ORDER_COMMA) || "null";
    return ["await " + b + "(" + c.join(", ") + ")", Blockly.JavaScript.ORDER_FUNCTION_CALL]
};

// ...
// when ask code
let code = Blockly['JavaScript'].workspaceToCode(workspace).replace(/(?<=^|\n)function \w+\(.*\)/g, 'async $&');

Just at a glance, I noticed in this code you will also need to update the ORDER used in 2 places (the change from ORDER_COMMA to ORDER_NONE is a recent fix in develop).

Blockly.JavaScript.procedures_callreturn = function(a) {
    for (var b = Blockly.JavaScript.variableDB_.getName(a.getFieldValue("NAME"), Blockly.JavaScript.PROCEDURE_CATEGORY_NAME), c = [], d = a.getVars(), e = 0; e < d.length; e++)
        c[e] = Blockly.JavaScript.valueToCode(a, "ARG" + e, Blockly.JavaScript.ORDER_NONE) || "null";
    return ["await " + b + "(" + c.join(", ") + ")", Blockly.JavaScript.ORDER_AWAIT]
};
maribethb commented 2 years ago

Moving this to samples as a feature request for a JavaScript generator that supports async/await