0

我做了一个块,其中发生对指定 URL 的请求。

在此块内,我可以处理来自响应的数据,但在此块外,由于异步,我无法获取此数据。

是否可以以块或其他方式模拟同步请求,将接收到的数据保存到全局变量中?

在此处输入图像描述

创建块的代码:

Blockly.Blocks['request'] =
'<block type="request">'
+ '     <value name="URL">'
+ '         <shadow type="text">'
+ '             <field name="TEXT">text</field>'
+ '         </shadow>'
+ '     </value>'
+ '     <value name="LOG">'
+ '     </value>'
+ '     <value name="WITH_STATEMENT">'
+ '     </value>'
+ '     <mutation with_statement="false"></mutation>'
+ '</block>';

Blockly.Blocks['request'] = {
init: function() {
    this.appendDummyInput('TEXT')
        .appendField('request');

    this.appendValueInput('URL')
        .appendField('URL');

    this.appendDummyInput('WITH_STATEMENT')
        .appendField('with results')
        .appendField(new Blockly.FieldCheckbox('FALSE', function (option) {
            var delayInput = (option == true);
            this.sourceBlock_.updateShape_(delayInput);
        }), 'WITH_STATEMENT');

    this.appendDummyInput('LOG')
        .appendField('log level')
        .appendField(new Blockly.FieldDropdown([
            ['none',  ''],
            ['info',  'log'],
            ['debug', 'debug'],
            ['warning',  'warn'],
            ['error', 'error']
        ]), 'LOG');

    this.setInputsInline(false);
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);

    this.setColour(230);
    this.setTooltip('Request URL');
    this.setHelpUrl('https://github.com/request/request');
},
mutationToDom: function() {
    var container = document.createElement('mutation');
    container.setAttribute('with_statement', this.getFieldValue('WITH_STATEMENT') === 'TRUE');
    return container;
},
domToMutation: function(xmlElement) {
    this.updateShape_(xmlElement.getAttribute('with_statement') == 'true');
},
updateShape_: function(withStatement) {
    // Add or remove a statement Input.
    var inputExists = this.getInput('STATEMENT');

    if (withStatement) {
        if (!inputExists) {
            this.appendStatementInput('STATEMENT');
        }
    } else if (inputExists) {
        this.removeInput('STATEMENT');
    }
}};

Blockly.JavaScript['request'] = function(block) {
var logLevel = block.getFieldValue('LOG');
var URL = Blockly.JavaScript.valueToCode(block, 'URL', Blockly.JavaScript.ORDER_ATOMIC);
var withStatement = block.getFieldValue('WITH_STATEMENT');

var logText;
if (logLevel) {
    logText = 'console.' + logLevel + '("request: " + ' + URL + ');\n'
} else {
    logText = '';
}

if (withStatement === 'TRUE') {
    var statement = Blockly.JavaScript.statementToCode(block, 'STATEMENT');
    if (statement) {

        var xmlhttp = "var xmlHttp = new XMLHttpRequest();";
        var xmlopen = "xmlHttp.open('POST', " + URL + ", true);";
        var xmlheaders = "xmlHttp.setRequestHeader('Content-type', 'application/json');\n" + 
                         "xmlHttp.setRequestHeader('Authorization', 'Bearer psokmCxKjfhk7qHLeYd1');";
        var xmlonload = "xmlHttp.onload = function() {\n" +
                        "  console.log('recieved:' + this.response);\n" +
                        "  var response = this.response;\n" +
                        "  var brightness = 'brightness: ' + JSON.parse(this.response).payload.devices[0].brightness;\n" +
                        "  " + statement + "\n" +
                        "}";

        var json = JSON.stringify({
            "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
            "inputs": [{
                "intent": "action.devices.QUERY",
                "payload": {
                    "devices": [{
                        "id": "0",
                        "customData": {
                            "smartHomeProviderId": "FkldJVJCmDNSaoLkoq0txiz8Byf2Hr"
                        }
                    }]
                }
            }]
        });

        var xmlsend = "xmlHttp.send('" + json + "');";

        var code = xmlhttp + '\n' + xmlopen + '\n' + xmlheaders + '\n' + xmlonload + '\n' + xmlsend;
        return code;

    } else {

        var xmlhttp = "var xmlHttp = new XMLHttpRequest();";
        var xmlopen = "xmlHttp.open('POST', " + URL + ", true);";
        var xmlheaders = "xmlHttp.setRequestHeader('Content-type', 'application/json');\n" + 
                         "xmlHttp.setRequestHeader('Authorization', 'Bearer psokmCxKjfhk7qHLeYd1');";
        var xmlonload = "xmlHttp.onload = function() {\n" +
                        "  console.log('recieved:' + this.response);\n" +
                        "}";

        var json = JSON.stringify({
            "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
            "inputs": [{
                "intent": "action.devices.QUERY",
                "payload": {
                    "devices": [{
                        "id": "0",
                        "customData": {
                            "smartHomeProviderId": "FkldJVJCmDNSaoLkoq0txiz8Byf2Hr"
                        }
                    }]
                }
            }]
        });

        var xmlsend = "xmlHttp.send('" + json + "');";

        var code = xmlhttp + '\n' + xmlopen + '\n' + xmlheaders + '\n' + xmlonload + '\n' + xmlsend;
        return code;
    }
} else {
        var xmlhttp = "var xmlHttp = new XMLHttpRequest();";
        var xmlopen = "xmlHttp.open('POST', " + URL + ", true);";
        var xmlheaders = "xmlHttp.setRequestHeader('Content-type', 'application/json');\n" + 
                         "xmlHttp.setRequestHeader('Authorization', 'Bearer psokmCxKjfhk7qHLeYd1');";
        var xmlonload = "xmlHttp.onload = function() {\n" +
                        "  console.log('recieved:' + this.response);\n" +
                        "}";

        var json = JSON.stringify({
            "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
            "inputs": [{
                "intent": "action.devices.QUERY",
                "payload": {
                    "devices": [{
                        "id": "0",
                        "customData": {
                            "smartHomeProviderId": "FkldJVJCmDNSaoLkoq0txiz8Byf2Hr"
                        }
                    }]
                }
            }]
        });

        var xmlsend = "xmlHttp.send('" + json + "');";

        var code = xmlhttp + '\n' + xmlopen + '\n' + xmlheaders + '\n' + xmlonload + '\n' + xmlsend;
        return code;
}};
4

2 回答 2

2

我们实际上在 Blockly 环境中广泛使用了 Promises。我的建议是 Promisify 这个然后使用生成器函数。例如,我们使用 co 库来包装我们生成的代码,然后对异步值使用 yield 语句以使它们同步运行。例如:

对于类似于此的块设置-

一个变量块将一个名为 getUsername 的变量设置为数据库中的值,在用户名中登录用户记录,然后向用户敬酒

生成的代码将是这样的(简化)——

co(function* () {
    var getUsername;
    // getFieldValue makes an asynchronous call to our database
    getUsername = (yield getFieldValue("username", "my user record Id", "users"));
    Promise.all(inputPromises)
    let inputPromises = [];
    inputPromises.push('User name is');
    inputPromises.push(getUsername);

    yield new Promise(function(resolve, reject) {
        Promise.all(inputPromises).then(function(inputResults) {
            let [TITLE, MESSAGE] = inputResults;
            let activity = "toastMessage";
            let LEVEL = "success";
            try {
                var params = {message: MESSAGE, title: TITLE, level: LEVEL};
                interface.notification(params);
                return resolve();
            } catch(err) {
                return reject(err);
            }

        }).catch(reject);

    });
    return true;
}

但是,正如您可能已经注意到的那样,这并不总是像在阻止之前坚持“让步”那么容易。根据您的设置,您可能需要使用 Promise.all 来获得更多的创意来获取块中的值等。(实际上,我们最终编辑了一堆 Blockly 核心块,以在输入前面附加“yield”在它们的输出类型中设置“promise”类型以使其工作,但根据您的设置,这可能是矫枉过正。)

显然,您需要确保它已被转译或在支持 ES6 的环境中运行。

当然,一旦你进入一个异步设置,就没有任何回头路了——协函数本身返回一个 Promise,所以你需要适当地处理它。但总的来说,我们发现这是一个非常强大的解决方案,我很乐意帮助您更详细地了解它。

于 2018-08-17T12:33:57.047 回答
1

通过使用 JS 解释器(文档GitHub ),您可以让块在没有 Promise、async函数或回调的情况下异步执行,该解释器由创建 Blockly 的同一个人方便地编写。JS Interpreter 是一个 JavaScript-in-JavaScript 实现。这确实意味着需要大量代码来将主 JS VM 的功能/命令连接到解释器的嵌入式实现。

Blockly 有一些这样做的演示(src)。特别是,您需要调查async-execution.html等待块的实现。你可以在这里找到正在运行的等待块。

方便的是,解释器的文档中有关 External API 的部分恰好XMLHttpRequest用作其示例实现。这应该是您的请求块实现的一个很好的起点。

于 2018-08-20T20:11:00.880 回答