前言
异步编程模式在前端开发过程中,显得越来越重要。从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题。随着ES6新规范的到来,解决异步数据流又有了新的计划。咱们都晓得,在传统的ajax申请中,当异步申请之间的数据存在依赖关系的时候,就可能产生很难看的多层回调,俗称'回调天堂'(callback hell),这却让人望而却步,Promise的呈现让咱们辞别回调函数,写出更优雅的异步代码。在实际过程中,却发现Promise并不完满,Async/Await是近年来JavaScript增加的最革命性的的个性之一,Async/Await提供了一种使得异步代码看起来像同步代码的代替办法。接下来咱们介绍这两种解决异步编程的计划。
一、Promise的原理与根本语法
1.Promise的原理
Promise 是一种对异步操作的封装,能够通过独立的接口增加在异步操作执行胜利、失败时执行的办法。支流的标准是 Promises/A+。
Promise中有几个状态:
- pending: 初始状态, 非 fulfilled 或 rejected;
- fulfilled: 胜利的操作,为表述不便,fulfilled 应用 resolved 代替;
- rejected: 失败的操作。
pending能够转化为fulfilled或rejected并且只能转化一次,也就是说如果pending转化到fulfilled状态,那么就不能再转化到rejected。并且fulfilled和rejected状态只能由pending转化而来,两者之间不能相互转换。
2.Promise的根本语法
- Promise实例必须实现then这个办法
- then()必须能够接管两个函数作为参数
- then()返回的必须是一个Promise实例
<script src="https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js"></script>//如果低版本浏览器不反对Promise,通过cdn这种形式<script>function loadImg(src) { var promise = new Promise(function(resolve, reject) { var img = document.createElement('img') img.onload = function() { resolve(img) } img.onerror = function() { reject('图片加载失败') } img.src = src }) return promise}var src = 'https://www.imooc.com/static/img/index/logo_new.png'var result = loadImg(src)result.then( function(img) { console.log(1, img.width) return img }, function() { console.log('error 1') } ).then(function(img) { console.log(2, img.height) })</script>
二、Promise多个串联操作
Promise还能够做更多的事件,比方,有若干个异步工作,须要先做工作1,如果胜利后再做工作2,任何工作失败则不再持续并执行谬误处理函数。要串行执行这样的异步工作,不必Promise须要写一层一层的嵌套代码。
有了Promise,咱们只须要简略地写job1.then(job2).then(job3).catch(handleError);
其中job1、job2和job3都是Promise对象。
比方咱们想实现第一个图片加载实现后,再加载第二个图片,如果其中有一个执行失败,就执行谬误函数:
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'var result1 = loadImg(src1) //result1是Promise对象var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'var result2 = loadImg(src2) //result2是Promise对象result1.then(function (img1) { console.log('第一个图片加载实现', img1.width) return result2 // 链式操作}).then(function (img2) { console.log('第二个图片加载实现', img2.width)}).catch(function (ex) { console.log(ex)})
这里需注意的是:then 办法能够被同一个 promise 调用屡次,then 办法必须返回一个 promise 对象。上例中result1.then如果没有明文返回Promise实例,就默认为自身Promise实例即result1,result1.then返回了result2实例,前面再执行.then实际上执行的是result2.then
三、Promise罕用办法
除了串行执行若干异步工作外,Promise还能够并行执行异步工作。
试想一个页面聊天零碎,咱们须要从两个不同的URL别离取得用户的个人信息和好友列表,这两个工作是能够并行执行的,用Promise.all()实现如下:
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1');});var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2');});// 同时执行p1和p2,并在它们都实现后执行then:Promise.all([p1, p2]).then(function (results) { console.log(results); // 取得一个Array: ['P1', 'P2']});
有些时候,多个异步工作是为了容错。比方,同时向两个URL读取用户的个人信息,只须要取得先返回的后果即可。这种状况下,用Promise.race()实现:
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1');});var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2');});Promise.race([p1, p2]).then(function (result) { console.log(result); // 'P1'});
因为p1执行较快,Promise的then()将取得后果'P1'。p2仍在继续执行,但执行后果将被抛弃。
总结:Promise.all承受一个promise对象的数组,待全副实现之后,对立执行success;
Promise.race承受一个蕴含多个promise对象的数组,只有有一个实现,就执行success
接下来咱们对下面的例子做下批改,加深对这两者的了解:
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'var result1 = loadImg(src1)var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'var result2 = loadImg(src2)Promise.all([result1, result2]).then(function(datas) { console.log('all', datas[0]) // console.log('all', datas[1]) //})Promise.race([result1, result2]).then(function(data) { console.log('race', data) //})
如果咱们组合应用Promise,就能够把很多异步工作以并行和串行的形式组合起来执行
四、Async/Await简介与用法
异步操作是 JavaScript 编程的麻烦事,很多人认为async函数是异步操作的终极解决方案。
1、Async/Await简介
- async/await是写异步代码的新形式,优于回调函数和Promise。
- async/await是基于Promise实现的,它不能用于一般的回调函数。
- async/await与Promise一样,是非阻塞的。
- async/await使得异步代码看起来像同步代码,再也没有回调函数。然而扭转不了JS单线程、异步的实质。
2、Async/Await的用法
- 应用await,函数必须用async标识
- await前面跟的是一个Promise实例
- 须要装置babel-polyfill,装置后记得引入 //npm i --save-dev babel-polyfill
function loadImg(src) { const promise = new Promise(function(resolve, reject) { const img = document.createElement('img') img.onload = function() { resolve(img) } img.onerror = function() { reject('图片加载失败') } img.src = src }) return promise}const src1 = 'https://www.imooc.com/static/img/index/logo_new.png'const src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'const load = async function() { const result1 = await loadImg(src1) console.log(result1) const result2 = await loadImg(src2) console.log(result2)}load()
当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作实现,再接着执行函数体内前面的语句。
五、Async/Await错误处理
await 命令前面的 Promise 对象,运行后果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。try..catch错误处理也比拟合乎咱们平时编写同步代码时候解决的逻辑。
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); }}
六、为什么Async/Await更好?
Async/Await较Promise有诸多益处,以下介绍其中三种劣势:
1. 简洁
应用Async/Await显著节约了不少代码。咱们不须要写.then,不须要写匿名函数解决Promise的resolve值,也不须要定义多余的data变量,还防止了嵌套代码。
2. 两头值
你很可能遇到过这样的场景,调用promise1,应用promise1返回的后果去调用promise2,而后应用两者的后果去调用promise3。你的代码很可能是这样的:
const makeRequest = () => { return promise1() .then(value1 => { return promise2(value1) .then(value2 => { return promise3(value1, value2) }) })}
应用async/await的话,代码会变得异样简略和直观
const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2)}
3.条件语句
上面示例中,须要获取数据,而后依据返回数据决定是间接返回,还是持续获取更多的数据。
const makeRequest = () => { return getJSON() .then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } })}
代码嵌套(6层)可读性较差,它们传播的意思只是须要将最终后果传递到最外层的Promise。应用async/await编写能够大大地进步可读性:
const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data }}