你不知道的JavaScript :Promise 与 Async/Await
前言
对于JavaScript
这门语言,其实我更喜欢称它为ECMAScript
,从一开始我们就已经涉及到异步编程,但是多数JavaScript开发者从来没有认真思考过自己程序中的异步,到底是怎么实现的,以及为什么会出现。但是由于开发者对JavaScript的需求和项目的复杂程度日渐扩大,特别是对异步的管理越来越令人痛苦,这一切导致我们迫切需要更加强大、更加合理的异步方法,帮我们管理异步的状态。
开始
在JavaScript异步编程历史上,我认为一共出现了三种异步编程方式
- 回调
- Promise
- Async/Await
由于我不想回忆起很久以前,被回调地狱
支配的恐惧,就跳过回调这一块,读者们自行了解(篇幅有限)
(PS:好吧,new Date() --> Sat May 19 2018 23:55:17 GMT+0800 (中国标准时间)
,写完洗洗睡吧)
Promise
什么是Promise
Promise是一种范式
。在这里扯一句:回调是将我们封装的回调函数交给第三方(甚至可能是外部代码),紧接着我们期待它能够调用我们封装的回调函数
。那么Promise就是不把自己的程序传递给第三方,而是第三方给我们提供此任务何时结束,然后由我们自己决定下一步做什么
。
点了一个汉堡
(做了一次请求
),我就在收银台上支付了这个汉堡的价格
(类似于参数的传递
),但是我并没有立即得到这个汉堡,而是拿到了一个订单号,这个时候我就只需要坐着等待小姐姐叫到我的订单号(这个时候已经产生了一个Promise
),在这个中间等待的时间,我们可以做一些其他的事情,比如打电话、玩儿手机、谈工作等等......(这就是合理利用中间的空隙时间,在js的异步中也可以完全体现,但不在本文的探讨范围内
),直到小姐姐叫到XX号码的好了(一个Promise执行结束
),结果一般有两种
,要么是汉堡做好了
(Promise中的resolve()中设置的值
),要么是汉堡卖完了
(Promise中的reject()中设置的值),这时我就需要考虑换其他食物了。假设汉堡做好了,我再过去拿我的汉堡,拿到过后,我可以自行选择吃掉它或者是扔掉它(当然这是不可能的)(这就体现了具体怎么实现的决定权在我们
)这个例子我觉得还是很形象的,哈哈哈。
如何创建并使用一个Promise
首先我先告诉你Promise的决议结果
:resovle
是完成
、reject
是拒绝
// -----------------------> Promise 的创建与使用 function creat_promise () { return new Promise((resolve, reject) => { resolve(42) }) } function use_promise () { const a_promise = creat_promise() a_promise.then(res => console.log(res)) } use_promise()
Promise的高级使用方法
当你看到这一节时,需要用到自定义的模拟数据请求类
该api类在这儿
或者查看本项目的源码,配合着浏览本文章
// api.js // 定义初始数据 const users = [ { id: 1, name: 'jack', year: 12, grade: 1 }, { id: 2, name: 'john', year: 12, grade: 1 }, { id: 3, name: 'winer', year: 12, grade: 2 } ] // user的father,根据child链接 const fathers = [ {id: 11, child: 'jack', name: 'jack_father'}, {id: 22, child: 'john', name: 'john_father'}, {id: 33, child: 'winer', name: 'winer_father'} ] class Api { // 根据id获取一个User对象的Promise getUserById (id, request_time = 1000, fn = function () {}) { return new Promise((resolve, reject) => { setTimeout(() => { const user = users.find(item => { return item.id === id }) fn() resolve(user) }, request_time) }) } // 根据grade获取一个User对象列表的Promise getUsersByGrade (grade) { return new Promise((resolve, reject) => { setTimeout(() => { const _users = users.filter(item => { return item.grade === grade }) resolve(_users) }, 1000) }) } // 根据user获取一个UserName的Promise getUserName (user, request_time = 1000) { return new Promise((resolve, reject) => { setTimeout(() => { const child = users.find(item => { return item.name === user.name }) resolve(child.name) }, request_time) }) } // 根据userName获取一个Father的Promise getFatherByChildName (childName) { return new Promise((resolve, reject) => { setTimeout(() => { const father = fathers.find(item => { return item.child === childName }) resolve(father) }, 1000) }) } // 抛出一个异常的Promise throw_Error () { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('api.js-------->抛出了一个错误')) }, 1000) }) } }
首先 const api = new Api()
Promise 的链式顺序模式
// -----------------------> Promise 的链式顺序模式 function promise_chain () { api.getUserById(1) .then(res => { console.log(res) return api.getUserName(res) }) .then(res => { console.log(res) return api.getFatherByChildName(res) }) .then(res => console.log(res)) } promise_chain()
每个Promise.then(..)中的返回值(
即使返回的不是Promise对象,因为Promise内在机制会将其转换为一个可以使用的Promise
)将会作为下一个Promise对象,即Promise.then( return ...)得到的是一个Promise对象
,因此可以不断地Promise.then( return ... ).then( return ... ).then( return ... ).then( return ... ).then( return ... ).then()......
Promise 的catch
function promise_lastErro () { api.throw_Error() .then(res => console.log('没有错')) .catch(err => { console.log(err) foo.bar() }) console.log('无法捕捉最后一个catch的错误:foo.bar()') } promise_lastErro()
Promise 的并发/并行 :
Promise.all([...])
传入的参数
是一个Promise的数组
如果传入的是[]
,Promise.all([...])将立即决议为完成
全部Promise完成,Promise.all([...])完成
如果有一个被拒绝,则Promise.all([...])被拒绝function promise_concurrent_1 () { const p1 = api.getUserById(1,Math.random() * 1000, () => { console.log('p1执行完毕') }) const p2 = api.getUserById(1,Math.random() * 1000, () => { console.log('p2执行完毕') }) Promise.all([p1, p2]) .then(res => console.log('p1 p2 都执行完毕')) } promise_concurrent_1()
Promise 的任务竞争:竞态(第一优先) :
Promise.race([...])
传入的参数
是一个Promise的数组
如果传入的是[]
,Promise.race([...])将不会决议
,始终处于挂起状态
只要有一个Primise完成,Promise.race([...])则完成
如果有一个被拒绝,则Promise.race([...])被拒绝function promise_compete () { const p1 = api.getUserById(1, 2000) const p2 = api.getUserById(2) Promise.race([p1, p2]).then(res => console.log(res)) } promise_compete()
关于Promise.all([...])与Promise.race([...])的变体有很多
- Promise.none([...]): 要求[...]
全部被拒绝
,Promise.none([...])决议为完成
- Promise.any([...]): 与Promise.all([...])类似,但只要求
完成一个
即可,但是会执行所有Promise - Promise.first([...]):与Promise.any([...])类似,但是
只要有第一个Promise决议为完成
,就不关心后面的Promise
了 - Promise.last([...]):与Promise.first([...])相反,
最后一个完成的胜出
- Promise.none([...]): 要求[...]
到此Promise的基本用法就是这些了吧,不知不觉半个小时过去了。
Async/Await
什么是Async/Await
我认为Async/Await
是区别于Promise更优雅的体现
,它可以简化Promise的大部分代码,让你的代码看上去优雅美观并且大气。并且我支持你,在现在甚至以后,对异步的管理尽可能使用Async/Await。
Async/Await的使用
使用Async/Await代替链式promise(类比 ---> Promise的链式顺序模式)
async function async_Request () { console.log('请稍等..此时是三个setTimeOut,每个1s,需要等待3s') const user = await api.getUserById(1) const userName = await api.getUserName(user) const father = await api.getFatherByChildName(userName) console.log(father) } async_Request()
使用Async/Await的并发与并行
async function async_Concurrent () { const users = await api.getUsersByGrade(1) const usersPromise = users.map(item => api.getUserName(item, Math.random() * 1000)) Promise.all(usersPromise).then(res => { console.log(res) }) } async_Concurrent()
使用Async/Await的错误捕捉
async function async_CatchErro () { try { await api.throw_Error() console.log('未捕捉到错误?') } catch (error) { console.log(error) } } async_CatchErro()
Async/Await函数的互相调用
async function async_A () { const user = await api.getUserById(2) const userName = await api.getUserName(user) const father = await api.getFatherByChildName(userName) return { user, userName, father } } async function async_B () { console.log('数据获取中...') const { user, userName, father } = await async_A() console.log('userInfo',{ user, userName, father }) } async_B()
Async/Await检索十条数据,串行
async function async_ten_serial (length = 10) { try { const users = [] console.log('串行请求10条数据,每条1秒,请稍等10秒钟....') while(users.length < 10) { users.push(await api.getUserById(1)) } console.log(users) } catch (error) { console.log(error) } } async_ten_serial()
Async/Await检索十条数据,并行
async function async_ten_parallel (length = 10) { try { const usersPromise = [] console.log('并行请求10条数据,每条1秒,请稍等1秒钟....') while(usersPromise.length < 10) { usersPromise.push(api.getUserById(2)) } const users = await Promise.all(usersPromise) console.log(users) } catch (error) { console.log(error) } } async_ten_parallel()
ok!本文并没有讲那些概念性的东西,只是简单地讲这几种实现用代码描述出来,更详细的。请大家参考官方文档,其实对于这篇文章的排版,我发现应该将Promise
与 Async/Await
对比起来描述,懒得重新排版了,委屈各位手动对比了。(:
源码地址
(PS: newDate() ---> Sun May 20 2018 00:42:15 GMT+0800 (中国标准时间)
)
不知不觉竟然写了接近一个小时,溜了溜了,不修仙。