【scratch-vm応用講座】スクラッチの拡張機能でもasync/await構文を使う方法


※ 当ページには【広告/PR】を含む場合があります。
2021/12/12
合同会社タコスキングダム|TacosKingdom,LLC.

スクラッチ3.0の自作エクステンションの作成に慣れてくると、色んなライブラリを読み込んでみたくなる時があります。

しかし残念ながらScratch-gui(正確にはScratch-vm)のデフォルトではjavascriptの非同期処理を扱う
Async/Await構文Promise関数を取り扱うことが出来ません。

でもご安心ください。

今回はスクラッチに非同期処理も行わせるコード修正の方法を説明していこうと思います。


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchを学べるオンライン・駅前プログラミングスクール5選

スクラッチではasync/awaitが呼び出せない問題

Scratch-guiを使って拡張機能をいじっていくとき、何気なくasync/awaitを使ったていたら、以下のエラーに遭遇してしまいました...。

            
            index.js:68 Uncaught ReferenceError: regeneratorRuntime is not defined
    at index.js:68
    at index.js:69
    at Object../node_modules/scratch-vm/src/extensions/*****/index.js (index.js:78)
    at __webpack_require__ (bootstrap:84)
    at Object.******** (extension-manager.js:19)
    at ExtensionManager.loadExtensionURL (extension-manager.js:153)
    at ExtensionLibrary.handleItemSelect (extension-library.jsx:43)
    at Object.wrapper [as onItemSelected] (index.js:550)
    at LibraryComponent.handleSelect (library.jsx:68)
    at Object.wrapper [as onSelect] (index.js:550)
        

原因を調べていくと、スクラッチというよりは
WebpackとBabelを使っていると起こる不具合のようです。

ではどうやってスクラッチからでも非同期処理を使えるようにするのかを解決策を解説していきましょう。


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchをしっかり学ぶためのオススメ書籍まとめ

Babelランタイムの追加

まずはscratch-vmに潜って、足りていないbabel関連のランタイムを追加しましょう。

            
            $ cd scratch-vm
$ yarn add @babel/runtime @babel/plugin-transform-runtime -D
        

これで準備完了です。

あとはこれをscratch-vmのエンドポイントとなっている
virtual-machine.jsに追加でインポートするとOKです。

            
            //👇とりあえずコードの冒頭に二行を追加
require('core-js');
require('regenerator-runtime/runtime');

//...以下略
        
これだけでスクラッチでもasync/await構文を利用できるようになりました。


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchを学べるオンライン・駅前プログラミングスクール5選

動作確認

せっかくなので簡単なasync/await構文を試せるだけの拡張機能を作って、動作してるか確認してみましょう。

拡張機能の作成手順は以前詳しく解説していた記事のほうを参考にしてください。

合同会社タコスキングダム|タコキンのPスクール
【Scratch拡張機能・自作】テキストデータを読み出し・保存するエクステンションを作ろう

Scratchの拡張機能の自作手順の基礎を、テキストを読み出し・書き出しできるような機能の実装を例に解説します。

エクステンションの自作の手順に従い、以下の
index.jsで簡単なコマンドブロックを作成してみます。

            
            const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');
const log = require('../../util/log');

class Scratch3AsyncTest {
    constructor (runtime) {
        this.runtime = runtime;
    }

    getInfo () {
        return {
            id: 'asynctest',
            name: 'Debug for testing Async/Await',
            blocks: [
                {
                    opcode: 'testAsync',
                    text: 'Async/Await tester',
                    blockType: BlockType.COMMAND
                }
            ],
            menus: {
            }
        };
    }

    //👇await出来ようにPromiseの関数を定義
    myFirstPromise(message) {
        console.log('一秒遅延しています...');
        return new Promise(resolve => {
            setTimeout(() => {
                resolve(message);
            }, 1000);
        })
    }

    //👇クラス内にasyncメンバ関数を定義
    async myFirstAsync() {
        const result = await this.myFirstPromise('はじめてのAsync/Awaitへようこそ!');
        return result;
    }

    testAsync () {
        //👇スクラッチブロックからでもasync関数が呼び出せるようになる
        this.myFirstAsync().then(result => {
            console.log(result);
        });
    }
}

module.exports = Scratch3AsyncTest;
        

以下のように作成したエクステンションのブロックをクリックするたびにブラウザのデバッグコンソールから表示できてるようならば成功です!

合同会社タコスキングダム|タコキンのPスクール


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchをしっかり学ぶためのオススメ書籍まとめ

まとめ

今回はScratch3.0でもasync/awaitとPromise関数が使えるように、Babelに不足していたランタイムを自分で補う方法を簡単に解説していきました。

今日日、既に非同期処理のライブラリも増えてきていますので、このやり方を知っておくとスクラッチアプリの応用の幅がもっと広がると考えます。

参考サイト

@babel/polyfill

Webpack + Babel で async/await 使ったら「regeneratorRuntime is not defined」エラー

Webpack v4 の babel-loader で async / await に対応させ、 IE11 にも対応させる設定