【ES6】Promise用法

ES6 - Promise用法

 

参考:https://www.jianshu.com/p/7e60fc1be1b2

https://www.jianshu.com/p/eb474a90cf46?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Promise概念

所谓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
更好的选择。
View Code

 

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, "========");
    })*/
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

 

<!--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>
map-distance.wxml

 

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


})
mao-distance.js

 

 

// 引入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
      });
  })
}
calculatePromise.js
// 引入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
        })
      }
    })
  })
}
myPromise.js

 

posted @ 2019-07-29 11:10  XJT2019  阅读(241)  评论(0编辑  收藏  举报