【ES6】Promise用法
ES6 - Promise用法
参考:https://www.jianshu.com/p/7e60fc1be1b2
Promise概念
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 Promise对象有以下两个特点。 (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。 注意,为了行文方便,本章后面的resolved统一只指fulfilled状态,不包含rejected状态。 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。 此外,Promise对象提供统一的接口,使得控制异步操作更加容易。 Promise也有一些缺点。首先,无法取消Promise ,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。 如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise 更好的选择。
Promise的构造函数接收一个参数(这个参数是一个函数),并且传入两个参数:resolve ,reject 分别表示异步操作执行成功后的回调函数
和异步执行失败后的回调函数。其实这里用‘成功’,‘失败’ 来描述并不准确,按照标准来讲,resolve 是将Promise的状态置为fullfiled
reject是将Promise的状态置为rejected。
01、基本用法
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例
Promise的基本结构
一步步的加载造成 回调地狱
/* //这种一步一步的加载 会造成回调地狱 var img=new Image(); img.src="./img/3-.jpg"; img.onload=function () { var img=new Image(); img.src="./img/4-/jpg"; img.onload=function () { var img=new Image(); img.src="./img/5-/jpg"; img.onload=function () { var img=new Image(); img.src="./img/6-/jpg"; } } }*/
基本结构:
/*var promise = new Promise(function (resolve, reject) { var img = new Image(); img.src = "./img/3-.jpg"; img.onload = function () { //加载成功,resolve(this) 传参数this resolve(this); }; img.onerror = function () { //加载失败 reject("加载失败"); } }); //此处是实例化了promise,也可以直接 .then() 连接 promise.then(function (data) { console.log(data, "__________"); }, function (error) { console.log(error, "========"); })*/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function loadImg(src) { return new Promise(function (res, rej) { var img = new Image(); img.onload = function () { res(this); }; img.onerror = function () { rej("加载错误"); }; img.src = src; }); } var arr = []; loadImg("./img/3-.jpg").then(function (data) { arr.push(data); return loadImg("./img/4-.jpg"); }, function (error) { console.log(error); }).then(function (data) { console.log(data); //<img src='./img/4-.jpg'> arr.push(data); console.log('arr===>',arr); }, function (error) { console.log(error); })
异步加载图片
function getImage(src) { return new Promise(function (res,rej) { let img=new Image(); img.src=src; img.onload=function () { //加载成功 res(img); }; img.onerror=function () { //加载错误 rej("加载错误") } }) }
实例化一个对象
/*let promise=getImage("./img/3-.jpg"); let promise1=promise.then(res => { console.log(this); console.log('res--->',res); return getImage("./img/4-.jpg"); },rej=> { console.log(rej); }); let promise2=promise1.then(res=> { console.log(this); console.log(res); return getImage("./img/5-.jpg") }); let promise3=promise2.then(res=> { console.log(this); console.log(res); return getImage("./img/6-.jpg") });*/
链式异步
/* * 链式异步 * * */ /*getImage("./img/3-.jpg").then(function (data) { console.log(this); console.log(data); return getImage("./img/4-.jpg"); }).then(res=> { console.log(this); console.log(res); return getImage("./img/5-.jpg"); }).then(function (data) { console.log(this); return getImage("./img/6-.jpg") })*/
.then(res=>{},rej=>{})
/* then有两个参数,都是函数,第一个函数是成功调用函数,第二个函数是失败调用函数 getImage("./img/1-.jpg").then(function (data) { console.log(data); },function (err) { console.log(err); })*/ // catch :失败后执行方法,有一个参数,这个参数是函数,失败后执行 /* getImage("./img/1-.jpg").then(function (data) { console.log(data); }).catch(function (err) { console.log(err); })*/
Promise.all 执行多个异步数组。成功返回所有结果组成列表数组,失败的时候则返回最先被reject失败状态的值。
/* * Promise.all 执行多个异步数组。成功返回所有结果组成列表数组,失败的时候则返回最先被reject失败状态的值。 * * */ let list = []; for (let i = 3; i < 80; i++) { list.push(getImage("./img/" + i + "-.jpg")); } /*Promise.all(list).then(function (arr) { console.log(arr); arr.forEach(t=>console.log(t.src)); });*/
Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。
代码模拟:
let wake = (time) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`${time / 1000}秒后醒来`) }, time) }) } let p1 = wake(3000) let p2 = wake(2000) Promise.all([p1, p2]).then((result) => { console.log(result) // [ '3秒后醒来', '2秒后醒来' ] }).catch((error) => { console.log(error) })
需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
Promise.race
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
/* * race 赛跑 * 执行多个异步数组,谁先完成异步,返回谁 * * * */ Promise.race(list).then(function (data) { console.log(data); })
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') },1000) }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('failed') }, 500) }) Promise.race([p1, p2]).then((result) => { console.log(result) }).catch((error) => { console.log(error) // 打开的是 'failed' })
应用举例:微信小程序 异步调用微信地图API
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!--form表单--> <form bindsubmit="formSubmit"> <!--输入起点和终点经纬度坐标,格式为string格式--> <label>起点坐标: <input style="border:1px solid #000;" name="start" value="鼓楼"></input> </label> <!--多个终点位置示例:39.984060,116.307520;39.984060,116.507520--> <label>终点坐标: <input style="border:1px solid #000;" name="dest"value="火车站"></input> </label> <!--提交表单数据--> <button form-type="submit">计算距离</button> </form> <!--渲染起点经纬度到终点经纬度距离,单位为米--> <view wx:for="{{distance}}" wx:key="index"> <view>起点到终点{{index+1}}的步行距离为{{item}}米</view> </view>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import myPromise from '../../utils/myPromise.js' import calculatePromise from '../../utils/calculatePromise.js' Page({ /** * 页面的初始数据 */ data: { distance: [], startPos:{}, destPos:{} }, //在Page({})中使用下列代码 //事件触发,调用接口 formSubmit(e) { //将输入的地名转化为经纬度 const startPlace = e.detail.value.start const destPlace = e.detail.value.dest myPromise({addr:startPlace}).then(res=>{ console.log(res) this.setData({ startPos: res.result.location }) }).then( myPromise({ addr: destPlace }).then(res => { console.log(res) this.setData({ destPos: res.result.location }) }) ).then(res2 => { console.log('===>', this.data) const newStartPos = `${this.data.startPos.lat},${this.data.startPos.lng}` const newDestPos = `${this.data.destPos.lat},${this.data.destPos.lng}` console.log(newStartPos,newDestPos) calculatePromise({ start: newStartPos, dest: newDestPos }).then(res => { //成功后的回调 console.log('计算距离的成功回调::', res); var res = res.result; var dis = []; for (var i = 0; i < res.elements.length; i++) { dis.push(res.elements[i].distance); //将返回数据存入dis数组, } this.setData({ //设置并更新distance数据 distance: dis }); } ) }) } })
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 引入SDK核心类 var QQMapWX = require('../libs/qqmap-wx-jssdk.js'); // 实例化API核心类 var qqmapsdk = new QQMapWX({ key: '2BJBZ-TKDRU-NVMV4-2JAJC-VK2Y2-M7F3D' // 必填 }); export default function calculatePromise(options) { return new Promise((resolve, reject) => { //调用距离计算接口 qqmapsdk.calculateDistance({ //mode: 'driving',//可选值:'driving'(驾车)、'walking'(步行),不填默认:'walking',可不填 //from参数不填默认当前地址 //获取表单提交的经纬度并设置from和to参数(示例为string格式) from: options.start || '', //若起点有数据则采用起点坐标,若为空默认当前地址 to: options.dest, //终点坐标 success: resolve, fail:reject }); }) }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 引入SDK核心类 var QQMapWX = require('../libs/qqmap-wx-jssdk.js'); // 实例化API核心类 var qqmapsdk = new QQMapWX({ key: '2BJBZ-TKDRU-NVMV4-2JAJC-VK2Y2-M7F3D' // 必填 }); export default function myPromise(options) { return new Promise((resolve, reject) => { //解析地名 并转化为经纬度 qqmapsdk.search({ keyword: options.addr, success: res1 => { console.log('地名查询--->', res1); const address = res1.data[0].address qqmapsdk.geocoder({ address: address, success: resolve, fail: reject }) } }) }) }