其实就是顺序执行异步操作。在这里先抛出一个小问题,用递归方法计算链表的长度。链表是最简单的动态数据结构,也是非常适用于递归解法的一种数据结构。计算其长度也应该是基本技能了,在这里直接贴出代码
function calNodeListLength(head) {
// 首先要对临界值进行判断
if (head===null) {
return 0
}
return 1 + calNodeListLength(head.next)
}
那顺着这个思路去想,我们就有了一种顺序上传文件的实现方法:上传 n 个文件,等到一个头文件上传完毕后调用函数再处理 n-1 个文件,然后等到 n-1 文件中的头文件传递完毕后再去处理剩下 n-2 个文件....
// 使用回调函数
function uploadFiles(files) {
// 这里使用伪代码
ajax({
// 要传递的数据
data: files[0],
success: function() {
// 删除传递过的文件
files.shift()
// 处理剩下的文件
uploadFiles(files)
}
})
}
// 使用 promise 的回调
function uploadFile(files) {
new Promise(resolve=>{
ajax({
data: files[0],
success: function() {
resolve()
}
})
}).then(res=>{
files.shift()
uplpadFiles(files)
})
}
以上方法只是顺着递归求链表的思路延伸出来的,其实也有很多更好的方法,举这个例子也是想让自己和阅读此文章的人对数据结构和算法更加的重视,学习好数据结构和算法是基本,从而也要提高将其运用到实际的项目中的能力,知识永远也学不完,更多的还是要提高自己运用已学知识去解决问题的能力。当然也不是说新知识不重要,比如接下来的实现方法就是用的「新知识和新语法」
;(async () => {
try {
for (let file of files) {
// uploadFile 返回的是一个 promise
await uploadFile(file)
}
} catch (e) {
console.log('嘤嘤嘤')
}
})()
首先检查是否有缓存。比如在 chrome 的网址中输入 chrome://cache
,就可以看到缓存文件,如果找不到文件则证明无缓存
如果有缓存,判断缓存是否过期。一般通过两个字段,HTTP 1.0 中的 expires
「表明过期时间,但是因为客户端的时间和服务器时间有可能不同,会导致出现差错」和 HTTP 1.1 中的 cache-control
,首先查看是否有 cache-control
如果没有,则根据 expires
比较过期时间,如果有 cache-control
则根据 cache-control
的属性 max-age
「 设置缓存的最大有效时间,max-age
会覆盖掉 expires
」或 s-maxage
「用于代理缓存,会覆盖掉 max-age
和 expires
」,如果没有过期,则就使用客户端缓存,也就是「强缓存」。
如果没有缓存或者缓存已经过期,浏览器会发出一个 DNS 请求到本地 DNS 服务器,本地服务器也会首先去查看有没有缓存记录,如果有的话直接将 ip 返回。如果没有缓存,这里以 www.baidu.com 为例说明,事实上浏览器在请求 DNS 服务器时的真正网址是 www.baidu.com. , 最后的 . 对应的就是根服务器,因为每次默认请求都会添加 . , 因此为了方便用户都会将其省略掉。所以网址的解析过程就为 . -> com. -> baidu.com. -> www.baidu.com. ,由根服务器开始查询,如果没有找到则向 com 域名服务器查询,直到查询到。
现在一般 DNS 服务器优化都会用到「负载均衡」,引用阿里云的一句话
多台服务器经常被用做提供同一个服务,从而减轻单台服务器所承受的访问压力。这样分散每台服务器上的压力,将访问流量分配到多台服务器的方法就是负载均衡。
socket 「套接字」 : 两个进程要进行通讯基本前提就是能够唯一标识一个进程,PID 只在本地中唯一,网络中冲突非常大。因此网络中是使用 ip 地址 + 协议 + 端口号唯一标识网络中的进程。socket 也就是由这三个部分组成。
Tcp 三次握手连接
浏览器向服务器发送请求,服务器根据响应头判断是否包含缓存验证信息。利用的是 Last-Modified
, If-Modified-Since
和 ETag
,If-None-Match
, 浏览器第一次请求资源的时候,服务器会在其响应头上添加 last-mdified
或者 etag
, 当浏览器再次请求此资源时,会在请求头上附带上 if-modified-since
或 if-none-match
,其值就是对应的服务器响应值,然后服务器会比对对应值去判断文件是否改变,if-modified-since
是去比对文件的最后修改时间,但有可能存在文件时间修改,单文件内容没有变的情况,所以出现了 etag
「内容的标识符,内容修改就会变化」服务器判断,如果文件没有改变,服务器返回 304,不会返回资源,浏览器收到响应后再去读取缓存。
如果已过期则服务器重新读取发送资源或者操作数据库,然后返回相应信息
TCP 四次挥手断开连接,主动方可以是客户端也可以是服务器端,这里以浏览器为主动方解释
首先浏览器检查响应状态码,如果资源可缓存,则进行缓存,然后根据资源类型相应的去处理
浏览器解析和渲染过程可以同时进行,浏览器解析 HTML 文档构建 DOM 树, 解析 CSS 资源构建 COM 树,如果遇到图片、样式表、js 文件等启动下载。然后根据 DOM 树和 COM 树,DOM 与 COM 构造呈现树,然后进行布局并渲染绘制到屏幕上。
这里涉及到 「回流」 与 「重绘」,当盒模型的位置大小等属性确定后,浏览器会计算其大小和位置,这就是「回流」,当盒模型的外观和风格等不会影响布局的属性确定后,就会引发 「重绘」。
可以分为三个步骤
通过 documentFragment
将挂载目标的所有子节点进行「劫持」,经过统一处理之后再整体插入挂载目标
将「劫持」到的 dom 对象进行遍历,将有 v-model
属性名的元素和被 {}
包裹的文本提取出来赋值给 Vue 「实例对象」 的 data
对象中
如果 input
框上 v-model
属性,那么会调用其 change
方法,实时更新 data
,在「实例对象」的 data
对象中创建的值会用 defineProperty
对其 set
和 get
进行改写,在此步骤中 set
只需要更新属性的值。
使用订阅发布模式「观察者模式」,发布者发布通知 => 主题对象「包含所有订阅者」收到通知推送给订阅者=>订阅者执行相应操作
因此,data
中每有一个属性被创建,相应也会创建其主题对象 dep
, 在处理「劫持」到的 html 时,每当与 data
中的属性绑定会在其对应的 dep
对象中添加一个订阅者 watcher
,其中需要通过 Dep
定义一个全局的 target
属性暂存新建的 watcher
,添加后移除。 watcher
触发实例对象的 get
方法从而将 Dep.target
存储进去
其实我相信大家看过大大小小的面试图谱和面试题都已经很多遍了,但是从我个人而言,我只看和背是很难记住和记牢的,经常是上个星期记忆过的知识这个星期就说不连贯了,只能再找到别人的面试图谱去再去背,再去理解。
因此我推荐大家将看到的很好的总结结合自己的理解再去撰写一遍,碰到模糊的再去查阅相关的技术文章。通过整理别人优秀的总结,查阅相关模糊的知识后,自己就也能根据自己的理解去将一些知识点进行一些专业的描述。最后整理出适合自己的面试图谱,一方面提高了自己的写作水平,一方面也让知识的掌握不是须于表面,难以从自己的口中系统化的讲解出来。
觉得还可以的话可以给个小小的 star~
1
carlclone 2018-10-09 16:14:58 +08:00 via Android
赞👍
|
2
deepkolos 2018-10-10 08:36:26 +08:00
这样的顺序上传只是为了避免上传任务顺序和后端接收是顺序不一致, 目的是避免文件间顺序信息的丢失, 所以可以考虑, 弄一个上传任务池, 设置最大并发上传 N 个, 文件顺序就用过单独的数组来指定就好了, 兼顾了速度和顺序信息
|
3
hutchins OP @deepkolos 嗯嗯,递归只是一个思路,目的是想要说明要学会把简单的数据结构应用到实际中,肯定有更好的方法。🤗
|