🍣
Blocklyのカスタムブロックでasync/awaitを使って現在のBitcoinの価格を知りたい!
結論から
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/blockly/blockly.min.js"></script>
<script>
Blockly.Blocks['console_log'] = {
init: function () {
this.appendDummyInput()
.appendField(new Blockly.FieldTextInput("ログ"), "log")
.appendField("出力");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip("");
this.setHelpUrl("");
}
};
Blockly.JavaScript['console_log'] = function (block) {
const text_log = block.getFieldValue('log');
const code = 'console.log(\''+ text_log+'\');\n';
return code;
};
Blockly.Blocks['wait'] = {
init: function () {
this.appendDummyInput()
.appendField(new Blockly.FieldNumber(1), "sec")
.appendField("秒待つ");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip("");
this.setHelpUrl("");
}
};
Blockly.JavaScript['wait'] = function (block) {
const number_sec = block.getFieldValue('sec');
const code = 'await sleep('+ number_sec+');\n';
return code;
};
</script>
<title>Blockly test</title>
</head>
<body>
<!-- Toolboxのブロック -->
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
<block type="math_number">
<field name="NUM">123</field>
</block>
<block type="text"></block>
<block type="text_print"></block>
<block type="console_log"></block>
<block type="wait"></block>
</xml>
<!-- Workspace -->
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
<!-- 実行ボタン -->
<button onclick="run()">実行</button>
<script>
const demoWorkspace = Blockly.inject('blocklyDiv',
{
media: 'https://unpkg.com/blockly/media/',
toolbox: document.getElementById('toolbox')
});
function sleep(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time * 1000);
});
}
function run() {
const code = '(async () => {'+Blockly.JavaScript.workspaceToCode(demoWorkspace) + '})();';
console.log(code);
try {
eval(code);
} catch (e) {
alert(e);
}
}
</script>
</body>
</html>
大事な部分は
const code = '(async () => {'+Blockly.JavaScript.workspaceToCode(demoWorkspace) + '})();';
code生成したあとに、asyncで囲んで、即時実行しているところです。
カスタムブロックでは関数実行だけで、その関数は予め用意しておくとよいかなと思います。
せっかくなのでWebAPIを使って現在のビットコインの値段を調べてみよう
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/blockly/blockly.min.js"></script>
<script>
Blockly.Blocks['console_log'] = {
init: function () {
this.appendDummyInput()
.appendField(new Blockly.FieldTextInput("ログ"), "log")
.appendField("出力");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip("");
this.setHelpUrl("");
}
};
Blockly.JavaScript['console_log'] = function (block) {
const text_log = block.getFieldValue('log');
const code = 'console.log(\''+ text_log+'\');\n';
return code;
};
Blockly.Blocks['wait'] = {
init: function () {
this.appendDummyInput()
.appendField(new Blockly.FieldNumber(1), "sec")
.appendField("秒待つ");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(230);
this.setTooltip("");
this.setHelpUrl("");
}
};
Blockly.JavaScript['wait'] = function (block) {
const number_sec = block.getFieldValue('sec');
const code = 'await sleep('+ number_sec+');\n';
return code;
};
Blockly.Blocks['webapi_btc'] = {
init: function () {
this.appendDummyInput()
.appendField("現在のビットコインの価格(USD)");
this.setOutput(true, null);
this.setColour(230);
this.setTooltip("");
this.setHelpUrl("");
}
};
Blockly.JavaScript['webapi_btc'] = function (block) {
var code = 'await getBitcoinRate()';
return [code, Blockly.JavaScript.ORDER_NONE];
};
</script>
<title>Blockly test</title>
</head>
<body>
<!-- Toolboxのブロック -->
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
<block type="math_number">
<field name="NUM">123</field>
</block>
<block type="text"></block>
<block type="text_print"></block>
<block type="console_log"></block>
<block type="wait"></block>
<block type="webapi_btc"></block>
</xml>
<!-- Workspace -->
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
<!-- 実行ボタン -->
<button onclick="run()">実行</button>
<!-- axiosライブラリ -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const demoWorkspace = Blockly.inject('blocklyDiv',
{
media: 'https://unpkg.com/blockly/media/',
toolbox: document.getElementById('toolbox')
});
function sleep(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time * 1000);
});
}
// USD
async function getBitcoinRate() {
// axiosでBTCPriceのAPIを叩きます(少し時間がかかる)
const res = await axios.get('https://api.coindesk.com/v1/bpi/currentprice.json');
// 取得できたBTCのUSD価格を抜き出す
return res.data.bpi.USD.rate_float;
}
function run() {
const code = '(async () => {'+Blockly.JavaScript.workspaceToCode(demoWorkspace) + '})();';
console.log(code);
try {
eval(code);
} catch (e) {
alert(e);
}
}
</script>
</body>
</html>
注意点
codeを文字列で返すとき、最後セミコロン(;)があるとアラート出力するときに、 alert(await getBitcoinRate(););
となってしまってシンタックスエラーになってしまいます。
ここらへん全部対応できないことあるかもなぁ
Discussion