promise async await使用

1.Promise

(名字含义:promise为承诺,表示其余手段没法改变)javascript

Promise 对象表明一个异步操做,其不受外界影响,有三种状态:
  • Pending(进行中、未完成的)
  • Resolved(已完成,又称 Fulfilled)
  • Rejected(已失败)

promises的优点

1.解决回调地狱php

2.更好地进行错误捕获css

有时咱们要进行一些相互间有依赖关系的异步操做,好比有多个请求,后一个的请求须要上一次请求的返回结果。过去常规作法只能 callback 层层嵌套,但嵌套层数过多的话就会有 callback hell 问题。好比下面代码,可读性和维护性都不好的。html

firstAsync(function(data){
    //处理获得的 data 数据
    //....
    secondAsync(function(data2){
        //处理获得的 data2 数据
        //....
        thirdAsync(function(data3){
              //处理获得的 data3 数据
              //....
        });
    });
});

 

promise例子1前端

//建立一个Promise实例,获取数据。并把数据传递给处理函数resolve和reject。须要注意的是Promise在声明的时候就执行了。
var getUserInfo=new Promise(function(resolve,reject){
    $.ajax({
        type:"get",
        url:"index.aspx",
        success:function(){
            if(res.code=="ok"){
                resolve(res.msg)//在异步操做成功时调用
            }else{
                reject(res.msg);//在异步操做失败时调用
            }
        }
    });
})
//另外一个ajax Promise对象,
var getDataList=new Promise(function(resolve,reject){
    $.ajax({
        type:"get",
        url:"index.aspx",
        success:function(res){
            if(res.code=="ok"){
                resolve(res.msg)//在异步操做成功时调用
            }else{
                reject(res.msg);//在异步操做失败时调用
            }
        }
    });
})
//Promise的方法then,catch方法
getUserInfo.then(function(ResultJson){
    //经过拿到的数据渲染页面
}).catch(function(ErrMsg){
    //获取数据失败时的处理逻辑
})
//Promise的all方法,等数组中的全部promise对象都完成执行
Promise.all([getUserInfo,getDataList]).then(function([ResultJson1,ResultJson2]){
    //这里写等这两个ajax都成功返回数据才执行的业务逻辑
})

注意:成功的结果须要用resolve包裹,失败的结果须要用reject包裹 vue

 

promise例子2java

ajax请求node

        var getData=new Promise(function(resolve,reject){
          $.post("http://apptest.hcbkeji.com/php/option/activity/chevron_report_activity.php",  {flag: 'click',act:'临沂页面',page:'临沂上报活动'},
            function (res) {
              resolve(res)
            }
          );
        })

        getData.then(res=>{
          console.log(res)   //{"type":"ok"}
        })

promise例子3ios

            var test=new Promise((resolve,reject)=>{
              setTimeout(function(){
                resolve('hello world')
              },2000)
            })
            test.then(res=>{
              console.log(res)
            })
            console.log('虽然在后面,可是我先执行')
            //打印结果
            // 虽然在后面,可是我先执行
            // hello world

promise例子4git

            function mytest(){
              return new Promise((resolve,reject)=>{
                $.post("http://apptest.hcbkeji.com/php/option/activity/chevron_report_activity.php",  {flag: 'click',act:'临沂页面',page:'临沂上报活动'},
                  function (res) {
                    var res=JSON.parse(res)
                    resolve(res)
                  }
                );
              })
            }

            mytest().then(res=>{
              console.log(res)
            })
            console.log('虽然在后面,可是我先执行')

 

 

 

 

2.async

async使用

做为一个关键字放到函数前面,用于表示函数是一个异步函数,由于async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行

调用方法:async 函数也是函数,平时咱们怎么使用函数就怎么使用它,直接加方法名括号调用。

 

          async function test(){
            return 'hello world'
          }
          test().then(res=>{
            console.log(res)
          })
          console.log('虽然在后面,可是我先执行')
          //打印结果
          //虽然在后面,可是我先执行
          //hello world

首先打印 ‘虽然在后面,可是我先执行’ ,后执行 打印  ‘hello world’

 

async的做用:输出的是一个 Promise 对象

async function testAsync() {
 return "hello async";
}
let result = testAsync();
console.log(result)

打印结果Promise {<resolved>: "hello async"}    从结果中能够看到async函数返回的是一个promise对象,若是在函数中 return 一个直接量,async 会把这个直接量经过 Promise.resolve() 封装成 Promise 对象。

async function testAsync1() {
 console.log("hello async");
}
let result1 = testAsync1();
console.log(result1);

结果返回Promise.resolve(undefined) 由于使用async 可是函数没有return一个直接量

 

 async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,若是在函数中 return 一个直接量,async 会把这个直接量经过 Promise.resolve() 封装成 Promise 对象。

async 函数返回的是一个 Promise 对象,因此在最外层不能用 await 获取其返回值的状况下,咱们固然应该用原来的方式:then() 链来处理这个 Promise 对象。

Promise 的特色是无等待,因此在没有 await 的状况下执行 async 函数,它会当即执行,返回一个 Promise 对象,而且,毫不会阻塞后面的语句。这和普通返回 Promise 对象的函数并没有二致。

 

 

Promise 有一个resolved,这是async 函数内部的实现原理。若是async 函数中有返回一个值 ,当调用该函数时,内部会调用Promise.solve() 方法把它转化成一个promise 对象做为返回,但若是timeout 函数内部抛出错误呢? 那么就会调用Promise.reject() 返回一个promise 对象, 这时修改一下timeout 函数

复制代码
async function timeout(flag) {
    if (flag) {
        return 'hello world'
    } else {
        throw 'my god, failure'
    }
}
console.log(timeout(true))  // 调用Promise.resolve() 返回promise 对象。
console.log(timeout(false)); // 调用Promise.reject() 返回promise 对象。
复制代码

  控制台以下:

  若是函数内部抛出错误, promise 对象有一个catch 方法进行捕获。

timeout(false).catch(err => {
    console.log(err)
})

 

 

 3.await

由于 async 函数返回一个 Promise 对象,因此 await 能够用于等待一个 async 函数的返回值——这也能够说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不只仅用于等 Promise 对象,它能够等任意表达式的结果,因此,await 后面实际是能够接普通函数调用或者promise对象

注意:await 命令只能用在 async 函数之中,若是用在普通函数,就会报错。

function getSomething() {
    return "something";
}

async function testAsync() {
    return Promise.resolve("hello async");
}

async function test() {
    const v1 = await getSomething();   //await后接普通函数调用
    const v2 = await testAsync();      //await后接async promise对象
    console.log(v1, v2);
}

test();          //打印结果something hello async

await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。

若是它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

若是它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,而后获得 resolve 的值,做为 await 表达式的运算结果。

 await 必须用在 async 函数中的缘由。async 函数调用不会形成阻塞,它内部全部的阻塞都被封装在一个 Promise 对象中异步执行。

综合:上面已经说明了 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

 

 

使用promise和async await比较

使用promise

function takeLongTime(n){
  return new Promise(resolve=>{
    setTimeout(()=>resolve(n+200),n)
  })
}

function step1(n){
  console.log(`step1 with ${n}`)
  return takeLongTime(n);
}

function step2(n){
  console.log(`step2 with ${n}`)
  return takeLongTime(n);
}

function step3(n){
  console.log(`step3 with ${n}`)
  return takeLongTime(n);
}


function run(){
  console.time('run')
  const time1=300;
  step1(time1)
    .then(time2=>step2(time2))
    .then(time3=>step3(time3))
    .then(result=>{
      console.log(`resutlt is ${result}`)
      console.timeEnd('run')
    })
}

run()

打印结果

step1 with 300
step2 with 500
step3 with 700
run: 1504.652099609375ms

 

使用async await

function takeLongTime(n){
  return new Promise(resolve=>{
    setTimeout(()=>resolve(n+200),n)
  })
}

function step1(n){
  console.log(`step1 with ${n}`)
  return takeLongTime(n);
}

function step2(n){
  console.log(`step2 with ${n}`)
  return takeLongTime(n);
}

function step3(n){
  console.log(`step3 with ${n}`)
  return takeLongTime(n);
}


async function run(){
  console.time('run')
  const time1=300
  const time2=await step1(time1)
  const time3=await step1(time2)
  const result=await step1(time3)
  console.log(`result is ${result}`)
  console.timeEnd('run')
}

run()

结果和以前的 Promise 实现是同样的,可是这个代码看起来是否是清晰得多,几乎跟同步代码同样

 

await 命令后面的 Promise 对象,运行结果多是 rejected,因此最好把 await 命令放在 try...catch 代码块中

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另外一种写法

async function myFunction() {
  await somethingThatReturnsAPromise().catch(function (err){
    console.log(err);
  });
}

 

async function dbFuc(db) {
  let docs = [{}, {}, {}];

  // 报错
  docs.forEach(function (doc) {
    await db.post(doc);
  });
}

 

await 关键字,await是等待的意思,那么它等待什么呢,它后面跟着什么呢?其实它后面能够听任何表达式,不过咱们更多的是放一个返回promise 对象的表达式。注意await 关键字只能放到async 函数里面

如今写一个函数,让它返回promise 对象,该函数的做用是2s 以后让数值乘以2

复制代码
// 2s 以后返回双倍的值
function doubleAfter2seconds(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2 * num)
        }, 2000);
    } )
}
复制代码

  如今再写一个async 函数,从而可使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,因此它后面能够写上 doubleAfter2seconds 函数的调用

async function testResult() {
    let result = await doubleAfter2seconds(30);
    console.log(result);
}

  如今调用testResult 函数

testResult();

  打开控制台,2s 以后,输出了60. 

  如今咱们看看代码的执行过程,调用testResult 函数,它里面遇到了await, await 表示等一下,代码就暂停到这里,再也不向下执行了,它等什么呢?等后面的promise对象执行完毕,而后拿到promise resolve 的值并进行返回,返回值拿到以后,它继续向下执行。具体到 咱们的代码, 遇到await 以后,代码就暂停执行了, 等待doubleAfter2seconds(30) 执行完毕,doubleAfter2seconds(30) 返回的promise 开始执行,2秒 以后,promise resolve 了, 并返回了值为60, 这时await 才拿到返回值60, 而后赋值给result, 暂停结束,代码才开始继续执行,执行 console.log语句。

  就这一个函数,咱们可能看不出async/await 的做用,若是咱们要计算3个数的值,而后把获得的值进行输出呢?

复制代码
async function testResult() {
    let first = await doubleAfter2seconds(30);
    let second = await doubleAfter2seconds(50);
    let third = await doubleAfter2seconds(30);
    console.log(first + second + third);
}
复制代码

  6秒后,控制台输出220, 咱们能够看到,写异步代码就像写同步代码同样了,再也没有回调地域了。

 

补充:在vue中使用

设计使用场景,一个手机充值活动,实现选择用户的省市,点击充值按钮,弹出相应省份的充值列表,2个请求。

请求1 获取所在省市:            根据手机号获得省和市            方法命名为getLocation    接受一个参数phoneNum             返回结果 province 和city

请求2 获取可充值面值列表:根据省和市获得充值面值列表   方法命名为getFaceList     接受两个参数province 和city     返回充值列表 

咱们首先要根据手机号获得省和市,因此写一个方法来发送请求获取省和市,方法命名为getLocation, 接受一个参数phoneNum ,当获取到城市位置之后,咱们再发送请求获取充值面值,因此还要再写一个方法getFaceList, 它接受两个参数, province 和city, 

  methods: {
      //获取到城市信息
      getLocation(phoneNum) {
          return axios.post('phoneLocation', {phoneNum:phoneNum})
      },
      // 获取面值
      getFaceList(province, city) {
          return axios.post('/faceList', {province:province,city:city})
      },
      // 点击肯定按钮时,显示面值列表
      getFaceResult () {
        this.getLocation(this.phoneNum)
          .then(res => {
            if (res.code=='ok') {
                let province = res.data.obj.province;
                let city = res.data.obj.city;
                this.getFaceList(province, city)
                    .then(res => {
                        if(res.code=='ok') {
                          //最终获取到面值列表
                          this.faceList = res.data.obj
                        }
                    })
            }
      })
      .catch(err => {
          console.log(err)
      })          
    }
  }

如今点击肯定按钮,能够看到页面中输出了 从后台返回的面值列表。这时你看到了then 的链式写法,有一点回调地域的感受。如今咱们在有async/ await 来改造一下。

首先把 getFaceResult 转化成一个async 函数,就是在其前面加async, 由于它的调用方法和普通函数的调用方法是一致,因此没有什么问题。而后就把 getLocation 和

getFaceList 放到await 后面,等待执行, getFaceResult  函数修改以下
  methods: {
      //获取到城市信息
      getLocation(phoneNum) {
          return axios.post('phoneLocation', {phoneNum:phoneNum})
      },
      // 获取面值
      getFaceList(province, city) {
          return axios.post('/faceList', {province:province,city:city})
      },
      // 点击肯定按钮时,显示面值列表
      async getFaceResult () {
        let location = await this.getLocation(this.phoneNum);
        if (location.code=='ok') {
            let province = location.data.obj.province;
            let city = location.data.obj.city;
            let result = await this.getFaceList(province, city);
            if (result.code=='ok') {
                this.faceList = result.data.obj;
            }
        }        
      }
  }

如今代码的书写方式,就像写同步代码同样,没有回调的感受,很是舒服。

如今就还差一点须要说明,那就是怎么处理异常,若是请求发生异常,怎么处理? 它用的是try/catch 来捕获异常,把await 放到 try 中进行执行,若有异常,就使用catch 进行处理。

  methods: {
      //获取到城市信息
      getLocation(phoneNum) {
          return axios.post('phoneLocation', {phoneNum:phoneNum})
      },
      // 获取面值
      getFaceList(province, city) {
          return axios.post('/faceList', {province:province,city:city})
      },
      // 点击肯定按钮时,显示面值列表
      async getFaceResult () {
          try {
              let location = await this.getLocation(this.phoneNum);
              if (location.code=='ok') {
                  let province = location.data.obj.province;
                  let city = location.data.obj.city;
                  let result = await this.getFaceList(province, city);
                  if (result.code=='ok') {
                      this.faceList = result.data.obj;
                  }
              }
          } catch(err) {
              console.log(err);
          }
      }
  }

 

 

 

 

参考:https://www.cnblogs.com/SamWeb/p/8417940.html

 

异步解决方案 async和await

前言

异步编程模式在前端开发过程当中,显得愈来愈重要。从最开始的XHR到封装后的Ajax都在试图解决异步编程过程当中的问题。随着ES6新标准的到来,处理异步数据流又有了新的方案。咱们都知道,在传统的ajax请求中,当异步请求之间的数据存在依赖关系的时候,就可能产生很难看的多层回调,俗称'回调地狱'(callback hell),这却让人望而生畏,Promise的出现让咱们告别回调函数,写出更优雅的异步代码。在实践过程当中,却发现Promise并不完美,Async/Await是近年来JavaScript添加的最革命性的的特性之一,Async/Await提供了一种使得异步代码看起来像同步代码的替代方法。接下来咱们介绍这两种处理异步编程的方案。

1、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 type="text/javascript">
        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>

2、Promise多个串联操做

Promise还能够作更多的事情,好比,有若干个异步任务,须要先作任务1,若是成功后再作任务2,任何任务失败则再也不继续并执行错误处理函数。要串行执行这样的异步任务,不用Promise须要写一层一层的嵌套代码。

有了Promise,咱们只须要简单地写job1.then(job2).then(job3).catch(handleError);
其中job一、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

3、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])//<img src="https://www.imooc.com/static/img/index/logo_new.png">
         console.log('all', datas[1])//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg">
     })
     Promise.race([result1, result2]).then(function (data) {
         console.log('race', data)//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg">
     })

若是咱们组合使用Promise,就能够把不少异步任务以并行和串行的方式组合起来执行

4、Async/Await简介与用法

异步操做是 JavaScript 编程的麻烦事,不少人认为async函数是异步操做的终极解决方案。

一、Async/Await简介

  • async/await是写异步代码的新方式,优于回调函数和Promise。

  • async/await是基于Promise实现的,它不能用于普通的回调函数。

  • async/await与Promise同样,是非阻塞的。

  • async/await使得异步代码看起来像同步代码,再也没有回调函数。可是改变不了JS单线程、异步的本质。

二、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 就会先返回,等到触发的异步操做完成,再接着执行函数体内后面的语句。

5、Async/Await错误处理

await 命令后面的 Promise 对象,运行结果多是 rejected,因此最好把 await 命令放在 try...catch 代码块中。try..catch错误处理也比较符合咱们日常编写同步代码时候处理的逻辑。

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

6、为何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    
  }
}

参考文章

Async/Await替代Promise的6个理由

前端的异步解决方案之Promise和Await/Async

廖雪峰的Javascript教程

[译] Promises/A+ 规范

async 函数的含义和用法

相关文章
相关标签/搜索