Как я могу прервать чтение, когда оно «зависает» (требуется тайм-аут для Reader.read())

#javascript #google-chrome #serial-port #webapi

#javascript #google-chrome #последовательный порт #webapi

Вопрос:

Этот вопрос связан с ситуацией, возникшей при использовании последовательного API Chrome, но, вероятно, может иметь отношение к любому ReadableStream. Я изучил документацию и, вероятно, пропустил какую-то функцию или шаблон.

В браузере Chrome запущена простая программа, получающая доступ к CW keyer (на основе Arduino, но это не важно).

Приложение отправляет команду ключу и ожидает два двоичных байта или строку в качестве ответа (конкретный формат зависит от отправленной команды и не имеет значения).

В случае, если последовательное устройство (не USB / последовательный адаптер, а Arduino) по какой-либо причине пропускает команду, ответ никогда не отправляется, и expectResponse() приведенная ниже функция никогда не вернет никаких данных и не выдаст никаких исключений. В результате считыватель остается заблокированным, поэтому ReadableStream не может быть закрыт и, как следствие, последовательный порт также не может быть закрыт.

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

 
async function expectResponse( serialPort ) {
   const reader = serialPort.readable.getReader() ;
   let { value, done } = await reader.read() ; // this never returns because no data arrive, not possible to "break"
}

async function disconnect( serialPort ) {
   // ... some cleanup ...
   // naive attempt to unlock ReadableStream before closing 
   await serialPort.readable.getReader().releaseLock() // this will throw exception - cannot create  new reader while another one is still active and locks the stream
   // ...
   await serialPort.close(); // this will throw exception - cannot close port because readable stream is locked
}
 

serialPort объект, возвращаемый navigator.serial.requestPort()

Я убежден, что, должно быть, пропустил что-то важное в документах API ( ReadableStream или Reader API, а не Serial API), но я не нашел решения.

PS в реальном приложении serialPort это глобальная переменная, но это не имеет значения, не так ли?

Ответ №1:

Я не думаю ReadableStream , что в него встроены тайм-ауты.

Я бы использовал Promise.race другое обещание, являющееся вашим таймаутом:

 let { value, done } = await Promise.race([
    reader.read(),
    new Promise((_, reject) => setTimeout(reject, TIMEOUT, new Error("timeout")))
]);
 

(Вы, вероятно, поместили бы этот new Promise код в служебную функцию.)

Promise.race наблюдает за выполнением обещаний, выполняя свое обещание на основе первого расчета, который он видит из обещаний в массиве, который вы ему даете. Итак, если read обещание выполнено (или отклонено) до отклонения обещания тайм-аута, read урегулирование определяет урегулирование race обещания. В противном случае race обещание выполняется на основе расчета (в данном случае отклонения) обещания тайм-аута.