Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
bf3492ef
V
vscode
项目概览
xxadev
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
bf3492ef
编写于
10月 07, 2020
作者:
J
Jackson Kearl
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Cache file tree in memory and batch transactions.
上级
78ad8e48
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
208 addition
and
92 deletion
+208
-92
src/vs/platform/files/browser/indexedDBFileSystemProvider.ts
src/vs/platform/files/browser/indexedDBFileSystemProvider.ts
+208
-92
未找到文件。
src/vs/platform/files/browser/indexedDBFileSystemProvider.ts
浏览文件 @
bf3492ef
...
@@ -8,7 +8,7 @@ import { IFileSystemProvider, IFileSystemProviderWithFileReadWriteCapability, Fi
...
@@ -8,7 +8,7 @@ import { IFileSystemProvider, IFileSystemProviderWithFileReadWriteCapability, Fi
import
{
Disposable
,
IDisposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
Disposable
,
IDisposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
VSBuffer
}
from
'
vs/base/common/buffer
'
;
import
{
VSBuffer
}
from
'
vs/base/common/buffer
'
;
import
{
joinPath
,
extUri
,
dirname
}
from
'
vs/base/common/resources
'
;
import
{
Throttler
}
from
'
vs/base/common/async
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
*
as
browser
from
'
vs/base/browser/browser
'
;
import
*
as
browser
from
'
vs/base/browser/browser
'
;
...
@@ -68,6 +68,90 @@ export class IndexedDB {
...
@@ -68,6 +68,90 @@ export class IndexedDB {
}
}
type
DirEntry
=
[
string
,
FileType
];
type
fsnode
=
|
{
path
:
string
,
type
:
FileType
.
Directory
,
parent
:
fsnode
|
undefined
,
children
:
Map
<
string
,
fsnode
>
,
}
|
{
path
:
string
,
type
:
FileType
.
File
,
parent
:
fsnode
|
undefined
,
size
:
number
|
undefined
,
};
const
readFromSuperblock
=
(
block
:
fsnode
,
path
:
string
)
=>
{
const
doReadFromSuperblock
=
(
block
:
fsnode
,
pathParts
:
string
[]):
fsnode
|
undefined
=>
{
if
(
pathParts
.
length
===
0
)
{
return
block
;
}
if
(
block
.
type
!==
FileType
.
Directory
)
{
throw
new
Error
(
'
Internal error reading from superblock -- expected directory at
'
+
block
.
path
);
}
const
next
=
block
.
children
.
get
(
pathParts
[
0
]);
if
(
!
next
)
{
return
undefined
;
}
return
doReadFromSuperblock
(
next
,
pathParts
.
slice
(
1
));
};
return
doReadFromSuperblock
(
block
,
path
.
split
(
'
/
'
).
filter
(
p
=>
p
.
length
));
};
const
deleteFromSuperblock
=
(
block
:
fsnode
,
path
:
string
)
=>
{
const
doReadFromSuperblock
=
(
block
:
fsnode
,
pathParts
:
string
[])
=>
{
if
(
pathParts
.
length
===
0
)
{
throw
new
Error
(
`Internal error deleting from superblock -- got no deletion path parts (encountered while deleting
${
path
}
)`
);
}
if
(
block
.
type
!==
FileType
.
Directory
)
{
throw
new
Error
(
'
Internal error reading from superblock -- expected directory at
'
+
block
.
path
);
}
block
.
children
.
delete
(
pathParts
[
0
]);
};
return
doReadFromSuperblock
(
block
,
path
.
split
(
'
/
'
).
filter
(
p
=>
p
.
length
));
};
const
addFileToSuperblock
=
(
block
:
fsnode
,
path
:
string
,
size
?:
number
)
=>
{
const
doAddFileToSuperblock
=
(
block
:
fsnode
,
pathParts
:
string
[])
=>
{
if
(
pathParts
.
length
===
0
)
{
throw
new
Error
(
`Internal error creating superblock -- adding empty path (encountered while adding
${
path
}
)`
);
}
if
(
block
.
type
!==
FileType
.
Directory
)
{
throw
new
Error
(
'
Internal error creating superblock -- adding entries to directory (encountered while adding ${path})
'
);
}
if
(
pathParts
.
length
===
1
)
{
const
next
=
pathParts
[
0
];
const
existing
=
block
.
children
.
get
(
next
);
if
(
existing
?.
type
===
FileType
.
Directory
)
{
throw
new
Error
(
`Internal error creating superblock -- overwriting directory with file:
${
block
.
path
}
/
${
next
}
(encountered while adding
${
path
}
)`
);
}
block
.
children
.
set
(
next
,
{
type
:
FileType
.
File
,
path
:
block
.
path
+
'
/
'
+
next
,
parent
:
block
,
size
,
});
}
else
if
(
pathParts
.
length
>
1
)
{
const
next
=
pathParts
[
0
];
let
childNode
=
block
.
children
.
get
(
next
);
if
(
!
childNode
)
{
childNode
=
{
children
:
new
Map
(),
parent
:
block
,
path
:
block
.
path
+
'
/
'
+
next
,
type
:
FileType
.
Directory
};
block
.
children
.
set
(
next
,
childNode
);
}
else
if
(
childNode
.
type
===
FileType
.
File
)
{
throw
new
Error
(
`Internal error creating superblock -- overwriting file entry with directory:
${
block
.
path
}
/
${
next
}
(encountered while adding
${
path
}
)`
);
}
doAddFileToSuperblock
(
childNode
,
pathParts
.
slice
(
1
));
}
};
doAddFileToSuperblock
(
block
,
path
.
split
(
'
/
'
).
filter
(
p
=>
p
.
length
));
};
class
IndexedDBFileSystemProvider
extends
Disposable
implements
IFileSystemProviderWithFileReadWriteCapability
{
class
IndexedDBFileSystemProvider
extends
Disposable
implements
IFileSystemProviderWithFileReadWriteCapability
{
readonly
capabilities
:
FileSystemProviderCapabilities
=
readonly
capabilities
:
FileSystemProviderCapabilities
=
...
@@ -81,8 +165,13 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
...
@@ -81,8 +165,13 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
private
readonly
versions
:
Map
<
string
,
number
>
=
new
Map
<
string
,
number
>
();
private
readonly
versions
:
Map
<
string
,
number
>
=
new
Map
<
string
,
number
>
();
private
readonly
dirs
:
Set
<
string
>
=
new
Set
<
string
>
();
private
readonly
dirs
:
Set
<
string
>
=
new
Set
<
string
>
();
constructor
(
private
readonly
scheme
:
string
,
private
readonly
database
:
IDBDatabase
,
private
readonly
store
:
string
)
{
private
superblock
:
Promise
<
fsnode
>
;
private
writeManyThrottler
:
Throttler
;
constructor
(
scheme
:
string
,
private
readonly
database
:
IDBDatabase
,
private
readonly
store
:
string
)
{
super
();
super
();
this
.
writeManyThrottler
=
new
Throttler
();
this
.
superblock
=
this
.
getSuperblock
();
this
.
dirs
.
add
(
'
/
'
);
this
.
dirs
.
add
(
'
/
'
);
}
}
...
@@ -98,25 +187,20 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
...
@@ -98,25 +187,20 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
}
}
}
catch
(
error
)
{
/* Ignore */
}
}
catch
(
error
)
{
/* Ignore */
}
// Make sure parent dir exists
await
this
.
stat
(
dirname
(
resource
));
this
.
dirs
.
add
(
resource
.
path
);
this
.
dirs
.
add
(
resource
.
path
);
}
}
async
stat
(
resource
:
URI
):
Promise
<
IStat
>
{
async
stat
(
resource
:
URI
):
Promise
<
IStat
>
{
try
{
const
content
=
readFromSuperblock
(
await
this
.
superblock
,
resource
.
path
);
const
content
=
await
this
.
readFile
(
resource
);
if
(
content
?.
type
===
FileType
.
File
)
{
return
{
return
{
type
:
FileType
.
File
,
type
:
FileType
.
File
,
ctime
:
0
,
ctime
:
0
,
mtime
:
this
.
versions
.
get
(
resource
.
toString
())
||
0
,
mtime
:
this
.
versions
.
get
(
resource
.
toString
())
||
0
,
size
:
content
.
byteLength
size
:
content
.
size
??
(
await
this
.
readFile
(
resource
)).
byteLength
};
};
}
catch
(
e
)
{
}
else
if
(
content
?.
type
===
FileType
.
Directory
||
this
.
dirs
.
has
(
resource
.
path
))
{
}
const
files
=
await
this
.
readdir
(
resource
);
if
(
files
.
length
)
{
return
{
return
{
type
:
FileType
.
Directory
,
type
:
FileType
.
Directory
,
ctime
:
0
,
ctime
:
0
,
...
@@ -124,75 +208,105 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
...
@@ -124,75 +208,105 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
size
:
0
size
:
0
};
};
}
}
if
(
this
.
dirs
.
has
(
resource
.
path
))
{
else
{
return
{
throw
createFileSystemProviderError
(
localize
(
'
fileNotExists
'
,
"
File does not exist
"
),
FileSystemProviderErrorCode
.
FileNotFound
);
type
:
FileType
.
Directory
,
ctime
:
0
,
mtime
:
0
,
size
:
0
};
}
}
throw
createFileSystemProviderError
(
localize
(
'
fileNotExists
'
,
"
File does not exist
"
),
FileSystemProviderErrorCode
.
FileNotFound
);
}
}
async
readdir
(
resource
:
URI
):
Promise
<
[
string
,
FileType
][]
>
{
async
readdir
(
resource
:
URI
):
Promise
<
DirEntry
[]
>
{
const
hasKey
=
await
this
.
hasKey
(
resource
.
path
);
const
entry
=
readFromSuperblock
(
await
this
.
superblock
,
resource
.
path
);
if
(
hasKey
)
{
if
(
!
entry
)
{
return
[];
}
throw
createFileSystemProviderError
(
localize
(
'
fileNotDirectory
'
,
"
File is not a directory
"
),
FileSystemProviderErrorCode
.
FileNotADirectory
);
if
(
entry
.
type
!==
FileType
.
Directory
)
{
throw
createFileSystemProviderError
(
localize
(
'
fileNotDir
'
,
"
File is not a Directory
"
),
FileSystemProviderErrorCode
.
FileNotADirectory
);
}
}
const
keys
=
await
this
.
getAllKeys
();
else
{
const
files
:
Map
<
string
,
[
string
,
FileType
]
>
=
new
Map
<
string
,
[
string
,
FileType
]
>
();
return
[...
entry
.
children
.
entries
()].
map
(([
name
,
node
])
=>
[
name
,
node
.
type
]);
for
(
const
key
of
keys
)
{
const
keyResource
=
this
.
toResource
(
key
);
if
(
extUri
.
isEqualOrParent
(
keyResource
,
resource
))
{
const
path
=
extUri
.
relativePath
(
resource
,
keyResource
);
if
(
path
)
{
const
keySegments
=
path
.
split
(
'
/
'
);
files
.
set
(
keySegments
[
0
],
[
keySegments
[
0
],
keySegments
.
length
===
1
?
FileType
.
File
:
FileType
.
Directory
]);
}
}
}
}
return
[...
files
.
values
()];
}
}
async
readFile
(
resource
:
URI
):
Promise
<
Uint8Array
>
{
async
readFile
(
resource
:
URI
):
Promise
<
Uint8Array
>
{
const
hasKey
=
await
this
.
hasKey
(
resource
.
path
);
const
read
=
new
Promise
<
Uint8Array
>
((
c
,
e
)
=>
{
if
(
!
hasKey
)
{
const
transaction
=
this
.
database
.
transaction
([
this
.
store
]);
throw
createFileSystemProviderError
(
localize
(
'
fileNotFound
'
,
"
File not found
"
),
FileSystemProviderErrorCode
.
FileNotFound
);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
}
const
request
=
objectStore
.
get
(
resource
.
path
);
const
value
=
await
this
.
getValue
(
resource
.
path
);
request
.
onerror
=
()
=>
e
(
request
.
error
);
if
(
typeof
value
===
'
string
'
)
{
request
.
onsuccess
=
()
=>
{
return
VSBuffer
.
fromString
(
value
).
buffer
;
if
(
request
.
result
instanceof
Uint8Array
)
{
}
else
{
c
(
request
.
result
);
return
value
;
}
else
if
(
typeof
request
.
result
===
'
string
'
)
{
}
c
(
VSBuffer
.
fromString
(
request
.
result
).
buffer
);
}
else
{
if
(
request
.
result
===
undefined
)
{
e
(
createFileSystemProviderError
(
localize
(
'
fileNotExists
'
,
"
File does not exist
"
),
FileSystemProviderErrorCode
.
FileNotFound
));
}
else
{
throw
createFileSystemProviderError
(
localize
(
'
internal
'
,
"
Internal error occured while reading file
"
),
FileSystemProviderErrorCode
.
Unknown
);
}
}
};
});
read
.
then
(
async
buffer
=>
addFileToSuperblock
(
await
this
.
superblock
,
resource
.
path
,
buffer
.
byteLength
),
()
=>
{
});
return
read
;
}
}
async
writeFile
(
resource
:
URI
,
content
:
Uint8Array
,
opts
:
FileWriteOptions
):
Promise
<
void
>
{
async
writeFile
(
resource
:
URI
,
content
:
Uint8Array
,
opts
:
FileWriteOptions
):
Promise
<
void
>
{
const
hasKey
=
await
this
.
hasKey
(
resource
.
path
);
try
{
if
(
!
hasKey
)
{
const
existing
=
await
this
.
stat
(
resource
);
const
files
=
await
this
.
readdir
(
resource
);
if
(
existing
.
type
===
FileType
.
Directory
)
{
if
(
files
.
length
)
{
throw
createFileSystemProviderError
(
localize
(
'
fileIsDirectory
'
,
"
File is Directory
"
),
FileSystemProviderErrorCode
.
FileIsADirectory
);
throw
createFileSystemProviderError
(
localize
(
'
fileIsDirectory
'
,
"
File is Directory
"
),
FileSystemProviderErrorCode
.
FileIsADirectory
);
}
}
}
}
catch
{
}
await
this
.
setValue
(
resource
.
path
,
content
);
this
.
fileWriteBatch
.
push
({
content
,
resource
});
await
this
.
writeManyThrottler
.
queue
(()
=>
this
.
writeMany
());
addFileToSuperblock
(
await
this
.
superblock
,
resource
.
path
,
content
.
byteLength
);
this
.
versions
.
set
(
resource
.
toString
(),
(
this
.
versions
.
get
(
resource
.
toString
())
||
0
)
+
1
);
this
.
versions
.
set
(
resource
.
toString
(),
(
this
.
versions
.
get
(
resource
.
toString
())
||
0
)
+
1
);
this
.
_onDidChangeFile
.
fire
([{
resource
,
type
:
FileChangeType
.
UPDATED
}]);
this
.
_onDidChangeFile
.
fire
([{
resource
,
type
:
FileChangeType
.
UPDATED
}]);
}
}
async
delete
(
resource
:
URI
,
opts
:
FileDeleteOptions
):
Promise
<
void
>
{
async
delete
(
resource
:
URI
,
opts
:
FileDeleteOptions
):
Promise
<
void
>
{
const
hasKey
=
await
this
.
hasKey
(
resource
.
path
);
let
stat
:
IStat
;
if
(
hasKey
)
{
try
{
await
this
.
deleteKey
(
resource
.
path
);
stat
=
await
this
.
stat
(
resource
);
this
.
versions
.
delete
(
resource
.
path
);
}
catch
(
e
)
{
this
.
_onDidChangeFile
.
fire
([{
resource
,
type
:
FileChangeType
.
DELETED
}]);
if
(
e
.
code
===
FileSystemProviderErrorCode
.
FileNotFound
)
{
return
;
return
;
}
throw
e
;
}
}
let
toDelete
:
string
[];
if
(
opts
.
recursive
)
{
if
(
opts
.
recursive
)
{
const
files
=
await
this
.
readdir
(
resource
);
const
tree
=
(
await
this
.
tree
(
resource
));
await
Promise
.
all
(
files
.
map
(([
key
])
=>
this
.
delete
(
joinPath
(
resource
,
key
),
opts
)));
toDelete
=
tree
.
map
(([
path
])
=>
path
);
}
else
{
if
(
stat
.
type
===
FileType
.
Directory
&&
(
await
this
.
readdir
(
resource
)).
length
)
{
throw
createFileSystemProviderError
(
localize
(
'
dirIsNotEmpty
'
,
"
Directory is not empty
"
),
FileSystemProviderErrorCode
.
Unknown
);
}
toDelete
=
[
resource
.
path
];
}
await
this
.
deleteKeys
(
toDelete
);
deleteFromSuperblock
(
await
this
.
superblock
,
resource
.
path
);
toDelete
.
forEach
(
key
=>
this
.
versions
.
delete
(
key
));
this
.
_onDidChangeFile
.
fire
(
toDelete
.
map
(
path
=>
({
resource
:
resource
.
with
({
path
}),
type
:
FileChangeType
.
DELETED
})));
}
private
async
tree
(
resource
:
URI
):
Promise
<
DirEntry
[]
>
{
if
((
await
this
.
stat
(
resource
)).
type
===
FileType
.
Directory
)
{
let
items
=
await
this
.
readdir
(
resource
);
await
Promise
.
all
(
items
.
map
(
async
([
key
,
type
])
=>
{
if
(
type
===
FileType
.
Directory
)
{
const
childEntries
=
(
await
this
.
tree
(
resource
.
with
({
path
:
key
})));
items
=
items
.
concat
(
childEntries
);
}
}));
items
=
items
.
concat
([[
resource
.
path
,
FileType
.
Directory
]]);
return
items
;
}
else
{
const
items
:
DirEntry
[]
=
[[
resource
.
path
,
FileType
.
File
]];
return
items
;
}
}
}
}
...
@@ -200,58 +314,60 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
...
@@ -200,58 +314,60 @@ class IndexedDBFileSystemProvider extends Disposable implements IFileSystemProvi
return
Promise
.
reject
(
new
Error
(
'
Not Supported
'
));
return
Promise
.
reject
(
new
Error
(
'
Not Supported
'
));
}
}
private
toResource
(
key
:
string
):
URI
{
return
URI
.
file
(
key
).
with
({
scheme
:
this
.
scheme
});
}
async
getAllKeys
():
Promise
<
string
[]
>
{
private
getSuperblock
():
Promise
<
fsnode
>
{
return
new
Promise
(
async
(
c
,
e
)
=>
{
return
new
Promise
((
c
,
e
)
=>
{
const
transaction
=
this
.
database
.
transaction
([
this
.
store
]);
const
transaction
=
this
.
database
.
transaction
([
this
.
store
]);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
request
=
objectStore
.
getAllKeys
();
const
request
=
objectStore
.
getAllKeys
();
request
.
onerror
=
()
=>
e
(
request
.
error
);
request
.
onerror
=
()
=>
e
(
request
.
error
);
request
.
onsuccess
=
()
=>
c
(
<
string
[]
>
request
.
result
);
});
}
hasKey
(
key
:
string
):
Promise
<
boolean
>
{
return
new
Promise
<
boolean
>
(
async
(
c
,
e
)
=>
{
const
transaction
=
this
.
database
.
transaction
([
this
.
store
]);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
request
=
objectStore
.
getKey
(
key
);
request
.
onerror
=
()
=>
e
(
request
.
error
);
request
.
onsuccess
=
()
=>
{
request
.
onsuccess
=
()
=>
{
c
(
!!
request
.
result
);
const
superblock
:
fsnode
=
{
children
:
new
Map
(),
parent
:
undefined
,
path
:
''
,
type
:
FileType
.
Directory
};
request
.
result
.
map
(
key
=>
key
.
toString
())
.
forEach
(
key
=>
addFileToSuperblock
(
superblock
,
key
));
c
(
superblock
);
};
};
});
});
}
}
getValue
(
key
:
string
):
Promise
<
Uint8Array
|
string
>
{
private
fileWriteBatch
:
{
resource
:
URI
,
content
:
Uint8Array
}[]
=
[];
return
new
Promise
(
async
(
c
,
e
)
=>
{
private
async
writeMany
()
{
const
transaction
=
this
.
database
.
transaction
([
this
.
store
]);
return
new
Promise
<
void
>
((
c
,
e
)
=>
{
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
fileBatch
=
this
.
fileWriteBatch
;
const
request
=
objectStore
.
get
(
key
);
this
.
fileWriteBatch
=
[];
request
.
onerror
=
()
=>
e
(
request
.
error
);
if
(
fileBatch
.
length
===
0
)
{
return
c
();
}
request
.
onsuccess
=
()
=>
c
(
request
.
result
||
''
);
});
}
setValue
(
key
:
string
,
value
:
Uint8Array
):
Promise
<
void
>
{
return
new
Promise
(
async
(
c
,
e
)
=>
{
const
transaction
=
this
.
database
.
transaction
([
this
.
store
],
'
readwrite
'
);
const
transaction
=
this
.
database
.
transaction
([
this
.
store
],
'
readwrite
'
);
transaction
.
onerror
=
()
=>
e
(
transaction
.
error
);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
request
=
objectStore
.
put
(
value
,
key
);
let
request
:
IDBRequest
=
undefined
!
;
request
.
onerror
=
()
=>
e
(
request
.
error
);
for
(
const
entry
of
fileBatch
)
{
request
=
objectStore
.
put
(
entry
.
content
,
entry
.
resource
.
path
);
}
request
.
onsuccess
=
()
=>
c
();
request
.
onsuccess
=
()
=>
c
();
});
});
}
}
deleteKey
(
key
:
string
):
Promise
<
void
>
{
private
deleteKeys
(
keys
:
string
[]
):
Promise
<
void
>
{
return
new
Promise
(
async
(
c
,
e
)
=>
{
return
new
Promise
(
async
(
c
,
e
)
=>
{
if
(
keys
.
length
===
0
)
{
return
c
();
}
const
transaction
=
this
.
database
.
transaction
([
this
.
store
],
'
readwrite
'
);
const
transaction
=
this
.
database
.
transaction
([
this
.
store
],
'
readwrite
'
);
transaction
.
onerror
=
()
=>
e
(
transaction
.
error
);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
objectStore
=
transaction
.
objectStore
(
this
.
store
);
const
request
=
objectStore
.
delete
(
key
);
request
.
onerror
=
()
=>
e
(
request
.
error
);
let
request
:
IDBRequest
=
undefined
!
;
for
(
const
key
of
keys
)
{
request
=
objectStore
.
delete
(
key
);
}
request
.
onsuccess
=
()
=>
c
();
request
.
onsuccess
=
()
=>
c
();
});
});
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录