Используйте переменную из обратного вызова в качестве глобальной переменной
Я сделал блок, в котором происходит запрос на указанный 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;
}};
2 ответа
На самом деле мы широко используем Promises в нашей среде Blockly. Мое предложение было бы обещать это, а затем использовать функцию генератора. Например, мы используем библиотеку co, чтобы обернуть наш сгенерированный код, а затем используем операторы yield для асинхронных значений, чтобы заставить их вести себя синхронно. Например:
Для настройки блока, подобной этой -
Сгенерированный код будет выглядеть примерно так (упрощенно) -
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" перед входами, которые имели Тип "обещание" установлен среди их типов вывода, чтобы заставить это работать, но в зависимости от ваших настроек это может быть излишним.)
Очевидно, вам необходимо убедиться, что это было либо передано, либо запущено в среде, которая поддерживает ES6.
Конечно, как только вы входите в асинхронную настройку, возвращаться некуда - сами функции co возвращают Promise, так что вам нужно будет соответствующим образом с этим справиться. Но в целом мы нашли, что это довольно надежное решение, и я рад помочь вам разобраться в этом более подробно.
Вы можете иметь блоки, которые выполняются асинхронно без обещаний, async
функции или обратные вызовы с использованием интерпретатора JS ( docs, GitHub), удобно написанного тем же человеком, который создал Blockly. JS Interpreter - это реализация JavaScript в JavaScript. Это означает, что для соединения функций / команд основной виртуальной машины JS со встроенной реализацией интерпретатора требуется много кода.
У Blockly есть несколько демонстраций этого ( src). В частности, вы хотите исследовать async-execution.html
и реализация блока ожидания. Вы можете найти живой блок ожидания здесь.
Удобно, что в разделе документа переводчика по внешнему API используется XMLHttpRequest
в качестве примера реализации. Это должно стать хорошей отправной точкой для реализации вашего блока запросов.