提前取消 setTimeout
Canceling setTimeout early
我正在制作一段录音 class,它可以运行一段指定的时间(例如 5 秒),也可以由用户提前停止。
我正在使用 setTimeout 来定义录制长度,这很有效。但是,我无法让 setTimeout 使用“停止”按钮。错误如下:
Cannot read properties of null (reading 'stop')
当 startRecording 函数执行时,会调用 handleStopRecording 函数,它使用“stopRecording”函数设置计时器。如果在时间到之前调用了“stopRecording”函数(通过按下“stop”按钮),那么最初在setTimeout中的函数调用仍然会在定时器到期时执行,从而导致错误。
我尝试使用 clearTimeout 解决这个问题,但是原始函数调用的“上下文”丢失了,我们得到了同样的错误:
Cannot read properties of null (reading 'stop')
除非我弄错了,否则我认为这是关闭 setTimeout 函数的问题 - 但是我不确定如何使用停止按钮提前清除该函数并限制录制时间。
提前致谢!
App.js (React.js)
import AudioRecorder from "./audioRecorder";
const App = () => {
const [recordedNameClipURL, setRecordedNameClipURL] = useState(null);
const [timeoutId, setTimeoutId] = useState(null);
const recorder = new AudioRecorder();
const startRecording = () => {
recorder.start();
handleStopRecording();
};
const handleStopRecording = async () => {
const id = setTimeout(stopRecording, 3000);
setTimeoutId(id);
};
const stopRecording = async () => {
clearTimeout(timeoutId);
const response = await recorder.stop();
setRecordedNameClipURL(response);
};
return (
...
);
};
audioRecorder.js
class AudioRecorder {
constructor() {
this.audioRecorder = null;
this.audioChunks = [];
}
initialize = async () => {
try {
await this.isSupported();
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.audioRecorder = new MediaRecorder(stream);
this.audioRecorder.addEventListener("dataavailable", event => {
this.audioChunks.push(event.data);
});
} catch (err) {
console.log(err.message);
}
};
start = async () => {
try {
await this.initialize();
this.audioRecorder.start();
} catch (err) {
console.log(err.message);
}
};
stop = async () => {
try {
this.audioRecorder.stop();
const blob = await this.stopStream();
return URL.createObjectURL(blob);
} catch (err) {
console.log(err.message);
}
};
stopStream = () => {
return new Promise(resolve => {
this.audioRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(this.audioChunks, {
type: this.audioRecorder.mimeType,
});
resolve(audioBlob);
});
});
};
isSupported = () => {
return new Promise((resolve, reject) => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
resolve(true);
}
reject(new Error("getUserMedia not supported on this browser!"));
});
};
}
export default AudioRecorder;
您可以尝试使用布尔值来检查进程是否已停止。您可以将其存储在状态中并在启动或停止时更改其值
const [isStopped, setIsStopped] = useState(false);
const handleStopRecording = async () => {
const id = setTimeout(() => {
if(!isStopped){
stopRecording
}
}, 3000);
setTimeoutId(id);
};
改为将计时器存储在 React Ref 中
我通常将 timeout/interval ID 存储在 React ref 中,因为存储句柄并不是像其他东西那样真正的“应用程序状态”。有时需要避免渲染抖动。
这是它的样子:
let timerRef = React.useRef(null)
const handleStopRecording = async () => {
timerRef.current = setTimeout(stopRecording, 3000)
}
const stopRecording = async () => {
clearTimeout(timerRef.current)
timerRef.current = null // good idea to clean up your data
const response = await recorder.stop()
setRecordedNameClipURL(response)
}
需要知道定时器是否运行的代码应该参考timerRef;不需要额外的状态:
let timerIsRunning = !!timerRef.current
我正在制作一段录音 class,它可以运行一段指定的时间(例如 5 秒),也可以由用户提前停止。
我正在使用 setTimeout 来定义录制长度,这很有效。但是,我无法让 setTimeout 使用“停止”按钮。错误如下:
Cannot read properties of null (reading 'stop')
当 startRecording 函数执行时,会调用 handleStopRecording 函数,它使用“stopRecording”函数设置计时器。如果在时间到之前调用了“stopRecording”函数(通过按下“stop”按钮),那么最初在setTimeout中的函数调用仍然会在定时器到期时执行,从而导致错误。
我尝试使用 clearTimeout 解决这个问题,但是原始函数调用的“上下文”丢失了,我们得到了同样的错误:
Cannot read properties of null (reading 'stop')
除非我弄错了,否则我认为这是关闭 setTimeout 函数的问题 - 但是我不确定如何使用停止按钮提前清除该函数并限制录制时间。
提前致谢!
App.js (React.js)
import AudioRecorder from "./audioRecorder";
const App = () => {
const [recordedNameClipURL, setRecordedNameClipURL] = useState(null);
const [timeoutId, setTimeoutId] = useState(null);
const recorder = new AudioRecorder();
const startRecording = () => {
recorder.start();
handleStopRecording();
};
const handleStopRecording = async () => {
const id = setTimeout(stopRecording, 3000);
setTimeoutId(id);
};
const stopRecording = async () => {
clearTimeout(timeoutId);
const response = await recorder.stop();
setRecordedNameClipURL(response);
};
return (
...
);
};
audioRecorder.js
class AudioRecorder {
constructor() {
this.audioRecorder = null;
this.audioChunks = [];
}
initialize = async () => {
try {
await this.isSupported();
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.audioRecorder = new MediaRecorder(stream);
this.audioRecorder.addEventListener("dataavailable", event => {
this.audioChunks.push(event.data);
});
} catch (err) {
console.log(err.message);
}
};
start = async () => {
try {
await this.initialize();
this.audioRecorder.start();
} catch (err) {
console.log(err.message);
}
};
stop = async () => {
try {
this.audioRecorder.stop();
const blob = await this.stopStream();
return URL.createObjectURL(blob);
} catch (err) {
console.log(err.message);
}
};
stopStream = () => {
return new Promise(resolve => {
this.audioRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(this.audioChunks, {
type: this.audioRecorder.mimeType,
});
resolve(audioBlob);
});
});
};
isSupported = () => {
return new Promise((resolve, reject) => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
resolve(true);
}
reject(new Error("getUserMedia not supported on this browser!"));
});
};
}
export default AudioRecorder;
您可以尝试使用布尔值来检查进程是否已停止。您可以将其存储在状态中并在启动或停止时更改其值
const [isStopped, setIsStopped] = useState(false);
const handleStopRecording = async () => {
const id = setTimeout(() => {
if(!isStopped){
stopRecording
}
}, 3000);
setTimeoutId(id);
};
改为将计时器存储在 React Ref 中
我通常将 timeout/interval ID 存储在 React ref 中,因为存储句柄并不是像其他东西那样真正的“应用程序状态”。有时需要避免渲染抖动。
这是它的样子:
let timerRef = React.useRef(null)
const handleStopRecording = async () => {
timerRef.current = setTimeout(stopRecording, 3000)
}
const stopRecording = async () => {
clearTimeout(timerRef.current)
timerRef.current = null // good idea to clean up your data
const response = await recorder.stop()
setRecordedNameClipURL(response)
}
需要知道定时器是否运行的代码应该参考timerRef;不需要额外的状态:
let timerIsRunning = !!timerRef.current