前端最基础的就是 HTML+CSS+Javascript
。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS
),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。
上一篇是令人吐槽的ES6,这一篇就有意思的多了。
异步操作我们不陌生,浏览器的UI事件、AJAX、Worker、setTimeout、setInterval 等等都是异步操作。
那么在执行异步操作时我们都会放入一个回调,这里有个名词叫什么来着?回调地狱。
哈哈当我们用 Promise 来处理的时候就简洁多了,可以链式操作。
回调与链式
举个栗子吧,请求 A,用 A 的响应去请求 B,用 B 的响应去渲染到页面上。
代码写下面了,思考几个问题
- 如果嵌套层级更深呢?
- 如果加上异常处理呢?
- 如果出现并行、串行、竞争等场景呢?
jQuery
中依赖 Deferred
实现。
回调形式(JQuery)
$.ajax({
url: 'https://www.lilnong.top/cors/A',
success(data){
$.ajax({
url: 'https://www.lilnong.top/cors/B',
data,
success(data){
$('body').text(JSON.stringify(data,null,4));
}
})
}
})
链式(Promise)(JQuery)
$.ajax({ url: 'https://www.lilnong.top/cors/A'})
.then((data)=>
$.ajax({url: 'https://www.lilnong.top/cors/B',data})
)
.then((data)=>
$('body')
.css('white-space', 'pre-wrap')
.text(JSON.stringify(data,null,8))
)
Promise
创建 Promise
new Promise(function(resolve, reject) {...});
同步调用 function
,里面可以是执行同步的,也可以执行异步的。
Promise状态
初始状态为 pending
当逻辑执行完之后调用 resolve(data)
(成功)、reject
(失败)来表示计算完成
调用之后 Promise 的状态会改变为 resolved
(完成)、rejected
(失败)。
如果内部抛出异常,也会进入 rejected
状态。
测试代码
new Promise((resolve, reject)=>{
var start_time = Date.now();
var dtime = Math.random()*1000;
setTimeout(()=>{
var end_time = Date.now();
var query_time = end_time - start_time
reject({start_time,dtime,end_time,query_time,now: Date.now()})
resolve({start_time,dtime,end_time,query_time,now: Date.now()})
console.log('promise', {start_time,dtime,end_time,query_time,now: Date.now()})
return 'promise-return'
}, dtime)
})
.then(v=>console.log('then',v))
.catch(v=>console.log('catch',v))
window.addEventListener('unhandledrejection',e=>console.log('event',e))
成功状态
状态不会被改变
失败状态
reject 触发状态改变
抛出异常
方法内抛出异常
没有catch,全局捕获
总结
-
function
中return
的没有被使用。 - 执行
resolve
、reject
并不会退出当前作用域,会继续执行下面的代码。 -
resolve
、reject
执行之后,状态和值就不会再被改变。即使报错都不会改变
-
resolve
、reject
执行之后,回调被放入微任务队列。 - 如果
reject
状态没有被catch
捕获,那么会触发unhandledrejection
。
Promise 方法
Promise.all(array)
返回一个新的 Promise 对象,根据 array 的结果来返回内容。
当队列中所有的 Promise 都成功才会触发成功。返回一个新的 Promise 对象,参数是个数组,是对应 Promise 的返回值。顺序是一 一对应的,和完成时间没关系。
当队列中有任何一个 Promise 失败则立即触发失败。返回一个新的 Promise 对象。Promise.all(Array) 返回一个 Promise 对象。
假设 PromiseAll = Promise.all(PromiseArray)
以下表格为 PromiseAll 在不同环境下的 不同状态与值。
PromiseAll 状态 | PromiseAll 值 | |
---|---|---|
所有 Promise 都成功 |
resolved | 按原数组顺序 组成的 promise 返回值集合 |
有一个 Promise 失败 |
rejected | 第一个失败的 Promise 的报错信息 |
依赖这个方法我们可以解决前言中提出的问题,处理并行场景。
Promise.race(array)
返回一个新的 Promise 对象,根据 array 的结果来返回内容。
当队列中任意一个 Promise 的状态变为完成状态(成功 resolved、失败 rejected),则返回该 Promise。
依赖这个方法我们可以解决前言中提出的问题,处理竞争场景。
Promise.reject(value)
返回一个状态为失败的 Promise 对象。
Promise.resolve(value)
返回一个状态由 value 决定的 Promise 对象。
如果 value 是个 Promise 对象,那么返回的就是该 Promise 对象。如果 value 不是 Promise 对象,那么返回就是一个成功状态的 Promise,值为 value。
如果你不知道一个值是不是 Promise 对象,使用Promise.resolve(value) 来返回一个 Promise 对象,这样就能安全的将该值当做 Promise 来使用了。
Promise.prototype.catch(onRejected)
当 Promise 失败时会调用。
return 返回值的会作为 resolve 状态继续传递。
Promise.prototype.then(onFulfilled, onRejected)
resolve 状态时会执行 onFulfilled 回调。
rejected 状态时会执行 onRejected 回调。如果没有 onRejected 回调 那么错误信息继续传递。
return 返回值的会作为 resolve 状态继续传递。
Promise.prototype.finally(onFinally)
resolve 和 rejected 状态都会调用回调。但是有不同点
- 回调没有参数。(resolve 的参数是传递或者 return 过来的,rejected 是传递或者抛出的异常。)
- 也不会覆盖之前值
其实可以当做 try{}catch(e){} finally{}
中 finally
来使用
await/async
await
操作符用于等待一个 Promise
对象。 await
只能在异步函数 async function
中使用。
上述的 Promise 只是把回调嵌套改成链式扁平化,但是看上去还是怪怪的,我们可以用 await 再处理一下 Promise。
var data = await fetch('https://www.lilnong.top/cors/A').then(v=>v.json())
var dataB = await fetch('https://www.lilnong.top/cors/B?'+new URLSearchParams(data)).then(v=>v.json())
console.log(dataB)
是不是看上去好理解多了。
课外知识
回调地狱
https://www.zhihu.com/question/49718514
async/await 地狱
这个我感觉是标题党蹭热度吧。。
主要说下面这个例子是地狱,当然,你一看这不是就上面那个例子吗?不就是串行执行吗(对,有的人并行的时候也这样写)?
console.log(startTime = Date.now())
dataA = await fetch('/')
console.log(Date.now(), Date.now()-startTime)
dataB = await fetch('/')
console.log(Date.now(), Date.now()-startTime)
并行解决方案
console.log(startTime = Date.now())
dataA = fetch('/')
console.log(Date.now(), Date.now()-startTime)
dataB = fetch('/')
await dataA
await dataB
console.log(Date.now(), Date.now()-startTime)
课后作业
爬一篇文章 https://p.51vv.com/vp/h/hot,遇到图片要下载,然后上传到服务器,用服务器返回的回填,执行完成输出文章JSON。
上面是需要实现的功能,下面来提提限制。
- 下载的图片要旋转、压缩、转换格式。
- 速度要尽可能的快,但是上传到服务器时需要串行。需要我解释一下为啥吗?因为图片有可能会有重复的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。