多个上下文访问 SharedArrayBuffer
时,如果同时对缓冲区执行操作,就可能出现资源争用问题。Atomics API
通过强制同一时刻只能对缓冲区执行一个操作,可以让多个上下文安全地读写一个SharedArrayBuffer
。
SharedArrayBuffer
和ArrayBuffer
具有相同的API,其区别为,前者可以被任意多个执行上下文同时使用,后者必须在不同的执行上下文中进行切换。
任何全局上下文中都有 Atomics 对象
,这个对象上暴露了用于执行线程安全操作的一套静态方法,其中多数方法以一个 TypedArray 实例
(一个 SharedArrayBuffer 的引用)作为第一个参数,以相关操作数作为后续参数。
AtomicReadModifyWrite 操作
。在底层,这些方法都会从 SharedArrayBuffer 中某个位置读取值,然后执行算术或位操作,最后再把计算结果写回相同的位置。这些操作的原子本质意味着上述读取、修改、写回操作会按照顺序执行,不会被其他线程中断。
Atomics.add()
Atomics.sub()
Atomics.or()
Atomics.and()
``Atomi
// 创建大小为 1 的缓冲区
let sharedArrayBuffer = new SharedArrayBuffer(1);
// 基于缓冲创建 Uint8Array
let typedArray = new Uint8Array(sharedArrayBuffer);
// 所有 ArrayBuffer 全部初始化为 0
console.log(typedArray); // Uint8Array[0]
const index = 0;
const increment = 5;
// 对索引 0 处的值执行原子加 5
Atomics.add(typedArray, index, increment);
console.log(typedArray); // Uint8Array[5]
// 对索引 0 处的值执行原子减 5
Atomics.sub(typedArray, index, increment);
console.log(typedArray); // Uint8Array[0]
浏览器的 JavaScript 编译器和 CPU 架构本身都有权限重排指令以提升程序执行效率,但多线程下的指令重排可能导致资源争用。
Atmoics API
通过两种主要的方式解决该问题
Atomics.load()
和 Atomics.store()
还可以构建“代码围栏”。JavaScript引擎保证非原子指令可以相对于 load()或 store()本地重排,但这个重排不会侵犯原子读/写的边界。Atomics.exchange()
执行简单的交换,以保证其他线程不会中断值的交换
const sharedArrayBuffer = new SharedArrayBuffer(4);
const view = new Uint32Array(sharedArrayBuffer);
// 在索引 0 处写入 3
Atomics.store(view, 0, 3);
// 从索引 0 处读取值,然后在索引 0 处写入 4
console.log(Atomics.exchange(view, 0, 4)); // 3
// 从索引 0 处读取值
console.log(Atomics.load(view, 0)); // 4
compareExchange()
方法只在目标索引处的值与预期值匹配时才会执行写操作
如果值不匹配,compareExchange()调用则什么也不做.
const sharedArrayBuffer = new SharedArrayBuffer(4);
const view = new Uint32Array(sharedArrayBuffer);
// 在索引 0 处写入 5
Atomics.store(view, 0, 5);
// 从缓冲区读取值
let initial = Atomics.load(view, 0);
// 对这个值执行非原子操作
let result = initial ** 2;
// 只在缓冲区未被修改的情况下才会向缓冲区写入新值
Atomics.compareExchange(view, 0, initial, result);
// 检查写入成功
console.log(Atomics.load(view, 0)); // 25
const sharedArrayBuffer = new SharedArrayBuffer(4);
const view = new Uint32Array(sharedArrayBuffer);
// 在索引 0 处写入 5
Atomics.store(view, 0, 5);
// 从缓冲区读取值
let initial = Atomics.load(view, 0);
// 对这个值执行非原子操作
let result = initial ** 2;
// 只在缓冲区未被修改的情况下才会向缓冲区写入新值
Atomics.compareExchange(view, 0, -1, result);
// 检查写入失败
console.log(Atomics.load(view, 0)); // 5
Atomics.wait(view, index, targetVal, time)
, 当索引index位置对应的值等于targetVal时,等待以获取锁,超时时间为time,超时顶部执行上下文会调用Atomics.notify()
释放其中一个等待的线程。
Atmoics.notify(view, index, count)
, 允许count个线程继续在index操作。
Atomics.isLockFree()
方法在高性能算法中可以用来确定是否有必要获取锁。
postMessage()方法
接收 3 个参数:消息
、表示目标接收源
的字符串和可选的可传输对象的数组(只与工作线程相关)
onmessage
的相应事件对象event包含三个方面的信息
Encoding API 主要用于实现字符串与定型数组之间的转换。规范新增了 4 个用于执行转换的全局类:TextEncoder
、TextEncoderStream
、TextDecoder
和 TextDecoderStream
。
Encoding API 提供了两种将字符串转换为定型数组二进制格式的方法:批量编码和流编码。把字符串转换为定型数组时,编码器始终使用 UTF-8.
批量编码
使用TextEncoder
的实例
encode()
,接受一个字符串参数,并以Uint8Array格式返回每个字符的UTF-8编码encodeInto()
,第一个参数为字符串str,第二个参数为目标Uint8Array,将str写入目标Uint8Array,返回一个字典,该字典包含read
和written
两个属性,分别表示成功读取和写入的字符。流编码
使用TextEncoderStream
的实例
批量解码
-----TextDecoder
decode()
, 不关心传入的是哪种定型数组,与 TextEncoder 不同,TextDecoder 可以兼容很多字符编码。流解码
-----TextDecoderStream
File API 仍然以表单中的文件输入字段为基础,但是增加了直接访问文件信息的能力。每个File对象包含以下只读属性
异步读取文件,具有以下的方法
readAsText(file, encoding)
, 从文件中读取纯文本内容,并保存在result
属性中readAsDataURL(file)
,读取文件并将内容的数据URL保存在result
属性中readAsBinaryString(file)
, 读取文件并将每个字符的二进制数据保存在result
属性中readAsArrayBuffer(file)
, 读取文件,并将内容以ArrayBuffer形式保存在result
属性中支持的事件处理程序
progress
, 表示还有更多数据,约没50毫秒触发一次,包含lengthComputable
、loaded
和 total
等信息,还可以通过result
属性读取已经读到的数据。error
, 发生错误,只包含一个信息code
。这个错误码的值可能是
load
, 读取完成FileReaderSync 类型就是 FileReader 的同步版本。这个类型拥有与 FileReader
相同的方法,只有在整个文件都加载到内存之后才会继续执行。FileReaderSync
只在工作线程中可用,因为如果读取整个文件耗时太长则会影响全局。
// worker.js
self.omessage = (messageEvent) => {
const syncReader = new FileReaderSync();
console.log(syncReader); // FileReaderSync {}
// 读取文件时阻塞工作线程
const result = syncReader.readAsDataUrl(messageEvent.data);
// PDF 文件的示例响应
console.log(result); // data:application/pdf;base64,JVBERi0xLjQK...
// 把 URL 发回去
self.postMessage(result);
};
读取部分文件,File对象
具有slice(startByteIndex, endByteIndex)
方法,返回一个Blob
实例。Blob
是File
的超类
blob 表示二进制大对象(binary larget object),是 JavaScript 对不可修改二进制数据的封装类型。包含字符串的数组、ArrayBuffers、ArrayBufferViews,甚至其他 Blob 都可以用来创建 blob。Blob构造函数可以接收一个 options 参数,并在其中指定 MIME 类型.
Blob 对象有一个 size
属性和一个 type
属性,还有一个 slice()
方法用于进一步切分数据,也可以使用 FileReader
从 Blob 中读取数据。
创建对象URL,使用 window.URL.createObjectURL()
方法并传入 File 或 Blob 对象。
释放对象URL, 使用window.URL.revokeObjectURL(url)
在页面创建放置目标后,被放置的文件可以通过事件的 event.dataTransfer.files
属性读到,这个属性保存着一组 File 对象。
必须取消 dragenter
、dragover
和drop
的默认行为
视频和音频的处理分别使用\
和\
,主要属性有src
/width
/height
/poster
/\
.
属性 | 数据类型 | 说明 |
---|---|---|
autoplay | boolean | 取得或者设置autoplay标签,下载完自动开始播放 |
buffered | TimeRanges | 表示已经下载缓冲的时间范围 |
bufferedBytes | ByteRanges | 表示已经下载缓冲的字节范围 |
bufferingRate | integer | 平均每秒下载的位数 |
bufferingThrottled | boolean | 表示缓冲是否有被浏览器截流 |
controls | boolean | 取得或设置controls属性,用于显示或者隐藏控件 |
currentLoop | integer | 已经循环播放的次数 |
currentSrc | string | 播放媒体的url |
currentTime | float | 已经播放的秒数 |
defaultPlaybackRate | float | 默认回放速率,默认1.0秒 |
duration | float | 总秒数 |
ended | booelan | 是否播放完 |
loop | boolean | 是否循环播放 |
muted | boolean | 是否静音 |
networkState | integer | 当前网络连接状态,0空,1加载中,2加载元数据,3加载了第一帧,4加载完成 |
paused | boolean | 是否暂停 |
playbackRate | float | 设置播放速率 |
played | TimeRanges | 已经播放的时间范围 |
readyState | integer | 是否已经准备就绪,0媒体不可用,1可以显示当前帧,2可以开始播放,3可以从头播放到尾 |
seekable | TimeRanges | 可以跳转的时间范围 |
seeking | boolean | 是否正移动到媒体的新位置 |
src | string | 媒体资源url,随时可以改写 |
start | float | 取得或者设置媒体文件中的位置,以秒为单位,从该处开始播放 |
totalBytes | integer | 总大小 |
videoHeight/videoWidth | integer | |
volume | float | 当前音量值,0.0-1.0 |
事件 | 何时触发 |
---|---|
abort | 下载被中断 |
canplay | 可以开始回放,readyState === 2 |
canplaythrough | 回放可以继续,readyState === 3 |
canshowcurrentframe | 已经下载当前帧,readyState === 1 |
dataunavailable | 不能回放,readyState=== 0 |
durationchange | duration属性发生变化 |
emptied | 网络连接关闭 |
empty | 发生了错误,组织媒体下载 |
ended | 媒体已经播放完一遍,并且停止了 |
error | 下载期间发生网络错误 |
loadeddata | 第一帧已经下载 |
loadedmetadata | 元数据已经下载 |
loadstart | 下载已经开始 |
pause | 回放已经暂停 |
paly | 媒体收到开始播放的请求 |
playing | 媒体已经开始播放 |
progress | 下载中 |
ratechange | 播放速率发生变化 |
seeked | 跳转已经结束 |
seeking | 回放已经移动到新的位置 |
stalled | 浏览器尝试下载,但是尚未收到数据 |
timeupdate | currentTime被非常规或者意外地修改了 |
volumechange | volume或者muted属性被修改 |
waiting | 回放暂停,以下载更多的数据 |
const audio = document.getElementById('audio-player');
if (audio.canPlayType("audio/mpeg"){
//...
}
if (audio.canPlayType("audio/ogg;codecs=\"vorbis\"")){
//...
}
Audio
构造函数,与Image
类似,但是不需要插入文档即可工作
let audio = new Audio("sound.mp3");
EventUtil.addHandler(audio, "canplaythrough", function(event){
audio.play();
});
被拖放元素的身上会依次触发dragstart
/drag
/dragend
目标元素身上会依次触发dragenter
/dragover
/dragleave或者drop
如果把元素拖动到不允许放置的目标上,无论用户动作是什么都不会触发 drop 事件。不过,通过event.preventDefault()
覆盖dragenter
和 dragover
事件的默认行为,可以把任何元素转换为有效的放置目标。
let droptarget = document.getElementById("droptarget");
droptarget.addEventListener("dragover", (event) => {
event.preventDefault();
//...
});
droptarget.addEventListener("dragenter", (event) => {
event.preventDefault();
//...
});
dataTransfer
对象,用于从被拖动元素向放置目标传递字符串数据。因为这个对象是event 的属性,所以在拖放事件的事件处理程序外部无法访问 dataTransfer
dataTransfer
对象有两个主要方法:getData()
和 setData()
。顾名思义,getData()
用于获取 setData()
存储的值。setData()的第一个参数以及 getData()的唯一参数是一个字符串
dataTransfer 对象
不仅可以用于实现简单的数据传输,还可以用于确定能够对被拖动元素和放置目标执行什么操作。为此,可以使用两个属性:dropEffect
与 effectAllowed
。
dropEffect 属性
可以告诉浏览器允许哪种放置行为,有以下 4 种可能的值:
none
, 被拖动元素不能放到这里。这是除文本框之外所有元素的默认值。move
, 被拖动元素应该移动到放置目标。copy
,被拖动元素应该复制到放置目标。link
, 表示放置目标会导航到被拖动元素(仅在它是 URL 的情况下)。为了使用 dropEffect
属性,必须在放置目标的 ondragenter
事件处理程序中设置它,并且同时设置 effectAllowed
,否则 dropEffect 属性也没有用
effectAllowed
属性表示对被拖动元素是否允许 dropEffect
,有如下几个可能的值:
uninitialized
, 没有给被拖动元素设置动作。none
, 被拖动元素上没有允许的操作。copy
, 只允许"copy"这种 dropEffect。link
, 只允许"link"这种 dropEffect。move
, 只允许"move"这种 dropEffect。copyLink
, 允许"copy"和"link"两种 dropEffect。copyMove
, 允许"copy"和"move"两种 dropEffect。linkMove
, 允许"link"和"move"两种 dropEffect。all
, 允许所有 dropEffect。ondragstart
事件处理程序中设置这个属性draggable 属性
,表示元素是否可以拖动。图片和链接的 draggable 属性自动被设置为 true
,而其他所有元素此属性的默认值为 false
。
HTML5 规范还为 dataTransfer
对象定义了下列方法:
addElement(element)
:为拖动操作添加元素。clearData(format)
:清除以特定格式存储的数据。setDragImage(element, x, y)
:允许指定拖动发生时显示在光标下面的图片。types
:当前存储的数据类型列表。