前端培训-中级阶段(30)- Promise 对象(2019-12-19期)

linong

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。

上一篇是令人吐槽的ES6,这一篇就有意思的多了。
异步操作我们不陌生,浏览器的UI事件、AJAX、Worker、setTimeout、setInterval 等等都是异步操作。
那么在执行异步操作时我们都会放入一个回调,这里有个名词叫什么来着?回调地狱
哈哈当我们用 Promise 来处理的时候就简洁多了,可以链式操作。

回调与链式

举个栗子吧,请求 A,用 A 的响应去请求 B,用 B 的响应去渲染到页面上。

代码写下面了,思考几个问题

  1. 如果嵌套层级更深呢?
  2. 如果加上异常处理呢?
  3. 如果出现并行串行竞争等场景呢?

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))

成功状态

image.png
状态不会被改变
image.png

失败状态

reject 触发状态改变

image.png

抛出异常

image.png

方法内抛出异常

image.png

没有catch,全局捕获

image.png

总结

  1. functionreturn 的没有被使用。
  2. 执行 resolvereject 并不会退出当前作用域,会继续执行下面的代码。
  3. resolvereject 执行之后,状态和值就不会再被改变。即使报错都不会改变
    image.png
  4. resolvereject 执行之后,回调被放入微任务队列
  5. 如果 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 状态都会调用回调。但是有不同点

  1. 回调没有参数。(resolve 的参数是传递或者 return 过来的,rejected 是传递或者抛出的异常。)
  2. 也不会覆盖之前值
    image.png

其实可以当做 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)

是不是看上去好理解多了。

课外知识

回调地狱

image.png
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。

上面是需要实现的功能,下面来提提限制。

  1. 下载的图片要旋转、压缩、转换格式。
  2. 速度要尽可能的快,但是上传到服务器时需要串行。需要我解释一下为啥吗?因为图片有可能会有重复的。

Promise PolyFill

如何写出一个惊艳面试官的 Promise【近 1W字】

es6-promise

微信公众号:前端linong

clipboard.png

参考文献

  1. 前端培训目录、前端培训规划、前端培训计划
  2. 使用 Promise - mdn
阅读 1.3k

javascript-lNong
只此一生,何必从众

Read-Search-Ask

22.7k 声望
4.8k 粉丝
0 条评论

Read-Search-Ask

22.7k 声望
4.8k 粉丝
文章目录
宣传栏