Использование async / await вместо обратного вызова при использовании Worklet - PullRequest
3 голосов
/ 26 апреля 2019

Я пишу класс-обертку, скрывающий внутреннюю часть работы с AudioWorklet. Работа с рабочим листом предполагает взаимодействие между узлом и процессором через порты сообщений.

Как только код, выполняющийся на узле, достигает port.postMessage (), выполнение сценария на узле заканчивается. Когда запускается node.port.onmessage (через processor.port.postMessage), код в узле может возобновить выполнение.

Я могу заставить его работать с помощью функции обратного вызова. Смотрите код ниже.

class HelloWorklet {
    constructor(audioContext) {
        audioContext.audioWorklet.addModule('helloprocessor.js').then(() => {
            this.awNode = new AudioWorkletNode(audioContext, 'hello-processor');
            this.awNode.port.onmessage = (event) => {
                switch (event.data.action) {
                case 'response message':
                    this.respondMessage(event.data);
                    break;
                }
            }
        });
    }
    requestMessage = (callback) => {
        this.awNode.port.postMessage({action: 'request message'});
        this.callback = callback;
    }
    respondMessage = (data) => {
        // some time consuming processing
        let msg = data.msg + '!';
        this.callback(msg);
    }
}

let audioCtx = new AudioContext();
let helloNode = new HelloWorklet(audioCtx);

const showMessage = (msg) => {
    // additional processing
    console.log(msg);
}

const requestMessage = () => {
    helloNode.requestMessage(showMessage);
}

и процессор

class HelloProcessor extends AudioWorkletProcessor {
    constructor() {
        super();

        this.port.onmessage = (event) => {
            switch (event.data.action) {
            case 'request message':
                this.port.postMessage({action: 'response message', msg: 'Hello world'});
                break;
            }
        }
    }

    process(inputs, outputs, parameters) {
        // required method, but irrelevant for this question
        return true;
    }
}
registerProcessor('hello-processor', HelloProcessor);

При вызове requestMessage() в консоли печатается Hello world!. Поскольку использование обратных вызовов иногда снижает читабельность кода, я хотел бы переписать код, используя await примерно так:

async requestMessage = () => {
    let msg = await helloNode.requestMessage;
    // additional processing
    console.log(msg);
}

Пытаясь переписать HelloWorklet.requestMessage, я не могу понять, как приклеить resolve из Promise к this.awNode.port.onmessage. Мне кажется, что прерывание кода между this.awNode.port.postMessage и this.awNode.port.onmessage выходит за пределы асинхронности.

Поскольку использование AudioWorklet уже нарушает обратную совместимость, можно использовать новейшие функции ECMAScript.

редактировать

Благодаря части 3 ответа Халеда Османа я смог переписать класс следующим образом:

class HelloWorklet {
    constructor(audioContext) {
        audioContext.audioWorklet.addModule('helloprocessor.js').then(() => {
            this.awNode = new AudioWorkletNode(audioContext, 'hello-processor');
            this.awNode.port.onmessage = (event) => {
                switch (event.data.action) {
                case 'response message':
                    this.respondMessage(event.data);
                    break;
                }
            }
        });
    }
    requestMessage = () => {
        return new Promise((resolve, reject) => {
            this.resolve = resolve;
            this.reject = reject;
            this.awNode.port.postMessage({action: 'request message'});
        })
    }
    respondMessage = (data) => {
        // some time consuming processing
        let msg = data.msg + '!';
        this.resolve(msg);
    }
}

let audioCtx = new AudioContext();
let helloNode = new HelloWorklet(audioCtx);

async function requestMessage() {
    let msg = await helloNode.requestMessage();
    // additional processing
    console.log(msg);
}

1 Ответ

1 голос
/ 26 апреля 2019

Я думаю, что есть три вещи, которые могут вам помочь

  1. Обещания не возвращают несколько значений, поэтому что-то вроде сообщения запроса не может быть снова запущено после его выполнения / разрешения, поэтому оно не будет подходящим для запроса / публикации нескольких сообщений. Для этого вы можете использовать Observables или RxJS

  2. Вы можете использовать util.promisify для преобразования функций стиля обратного вызова NodeJS в обещания, подобные

const { readFile } = require('fs')
const { promisify } = require('util')
const readFilePromise = promisify(fs.readFile)

readFilePromise('test.txt').then(console.log)

или вручную создайте функции-оболочки, которые возвращают обещания вокруг них, которые разрешают / отклоняют внутри обратных вызовов.

  1. Для разрешения обещания за пределами блока обещания вы можете сохранить разрешение / отклонение как переменные и вызвать их позже, например, так:
class MyClass {
  requestSomething() {
    return new Promise((resolve, reject) => {
      this.resolve = resolve
      this.reject = reject
    })
  }

  onSomethingReturned(something) {
    this.resolve(something)
  }
}
...