关于javascript:异步解决方案Promise与Await

39次阅读

共计 5689 个字符,预计需要花费 15 分钟才能阅读完成。

前言

异步编程模式在前端开发过程中,显得越来越重要。从最开始的 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]) //![](https://www.imooc.com/static/img/index/logo_new.png)
  console.log('all', datas[1]) //![](https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg)
})
Promise.race([result1, result2]).then(function(data) {console.log('race', data) //![](https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg)
})  

如果咱们组合应用 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    
  }
}

正文完
 0