一篇文章带你学会:js异步任务+Promise+async/await语法糖

同步和异步

  • 同步任务是指当前主线程将要消化执行的任务,这些任务一起形成执行栈
  • 异步任务是指不进入主线程,而是进入任务队列,即不会马上进行的任务

异步任务

同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。

异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行

 setTimeout(function(){
    console.log('执行了回调函数');
 },3000)
 console.log('111');

定时器函数不会阻塞打印语句的执行,这种不阻塞后面任务执行的任务就叫做异步任务

异步原理

  • 所有同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,还存在一个"任务队列"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  • 一旦"执行栈"中的所有同步任务执行完毕(★★★),系统就会读取"任务队列",那些对应的异步任务,于是结束等待状态进入执行栈,开始执行。
  • 主线程不断重复上面的第三步。

回调地狱

存在异步任务的代码,不能保证能按照顺序执行,那如果我们非要代码顺序执行呢?

 setTimeout(function () {  //第一层
    console.log('111');
    setTimeout(function () {  //第二程
           console.log('222');
           setTimeout(function () {   //第三层
               console.log('333');
        }, 1000)
    }, 2000)
}, 3000)

可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱

总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护

解决回调地狱

Promise

Promise是js中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案

  • Promise构造函数接收一个函数作为参数,我们需要处理的异步任务就写在该函数体内,该函数的两个参数是resolve,reject。异步任务执行成功时调用resolve函数返回结果,反之调用reject
  • Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据
  • Promise的链式编程可以保证代码的执行顺序,前提是每一次在then做完处理后,一定要return一个Promise对象,这样才能在下一次then时接收到数据

举个例子:

  • 现在有个函数fun1和fun2,它们都是异步任务
  • 现在要保证先执行fun1,在成功拿到fun1的返回值后,再调函数fun2
  • 并且将fun1返回值中的data作为参数传给fun2,如果fun1的code不等于200,就直接阻塞,不让代码继续执行
  • 并且还要保证打印代码的顺序执行
  created() {
    // 代码打印顺序: 1  -->  2  -->  haha
    // 当fun1接受的参数不等于字符串"黄家驹",代码阻塞
   	// 当fun1调取成功后,未将fun1的返回值data传给fun2,代码阻塞
    // 这样就保证了代码的顺序执行
    this.fun1("黄家驹").then(res => {
      if (res.code === 200) {
        console.log('1',res)
        this.fun2(res.data).then(res2 => {
          if (res2.code === 200) {
            console.log('2',res2)
            console.log("hahha")
          }
        })
      }
    })
  },
  methods: {
    fun1(str) {
      const p = new Promise(function(resolve,reject){
        // 模仿异步请求
        setTimeout(() => {
          if (str === "黄家驹") {
            const res = {
              code: 200,
              data: str + "拿到了吉他"
            }
            resolve(res)
          }else {
            const err = {
              code: 500,
              data: "黄家驹没有拿到吉他"
            }
            reject(err)
          }
        }, 1000)
      })
      return p
    },

    fun2(str) {
      const p = new Promise(function(resolve,reject){
        setTimeout(() => {
          if (str === "黄家驹拿到了吉他") {
            const res = {
              code: 200,
              data: str + ",并唱了首海阔天空"
            }
            resolve(res)
          }else {
            const err = {
              code: 500,
              data: "黄家驹拿到了一把坏吉他,无法演唱"
            }
            reject(err)
          }
        }, 2000)
      })
      return p
    },
}
  • 这样虽然能保证代码的顺序执行
  • 但是Promise最大的问题就是代码冗余,原来的异步任务被Promise封装一下,不管什么操作都用then,就会导致一眼看过去全是then…then…then…,这样也是不利于代码维护的。
  • 所以下面的async/await 可以时代码看起来更像同步代码。

async+await

当我们写代码遇到异步回调时,我们想让异步代码按照我们想要的顺序执行,如果按照传统的嵌套方式,就会出现回调地狱,这样的代码不利于维护,我们可以通过Promise对象进行链式编程来解决,这样尽管可以解决问题,但是ES7给我们提供了更加舒适的async/await语法糖,可以使得异步代码看起来更像是同步代码

async
  • async关键字,他作为一个关键字放到声明函数前面,表示该函数为一个异步任务,不会阻塞后面函数的执行

  • 和Promise对象一样(说明还是有不一样的),处理异步任务时也可以按照成功和失败来返回不同的数据,处理成功时用then方法来接收,失败时用catch方法来接收数据

  • async函数返回数据时自动封装为一个Promise对象

  • 使用async函数就不用resolve了,写return

// async基础语法
async function fun0(){
    console.log(1);
    return 100;
}
fun0().then(res=>{
    console.log(res) //  1   100
})

await

  • await关键字只能在使用async定义的函数中使用。可以理解为等待
  • await后面可以直接跟一个 Promise实例对象(可以跟任何表达式,更多的是跟一个返回Promise对象的表达式),await会等待这个promise的状态由pending转为fulfilled或者rejected。在此期间它会阻塞,延迟执行await语句后面的语句。
  • await函数不能单独使用
  • await可以直接拿到Promise中resolve中的数据

当代码执行到async函数中的await时,代码就在此处等待不继续往下执行(★★★),直到await拿到Promise对象中resolve的数据才继续往下执行,这样就保证了代码的执行顺序,而且使异步代码看起来更像同步代码

// 使用async/await的使用

// 定义一个异步函数,3秒后才能获取到值(类似通过接口获得数据库的数据)  类似于getUserArr
function getSomeThing(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('获取成功')
        },3000)
    })
}

async function test(){  // 类似于loadUserList
    let a = await getSomeThing();
    console.log(a);
    return a;   // 只有通过getSomeThing拿到a,才能return出去
}
test(); // 3秒后输出:获取成功

举个栗子

例一

注意:axios返回的就是一个promise对象

data() {
    return {
        count: 0,
    }
}

created() {
	this.initData()
},
    
methods: {
    async initData() {
      // 以下代码按照顺序执行,当test或者test2函数报错时,就会阻塞代码执行
      console.log(111)
      await this.test()
      console.log(222)
      await this.test2()
      console.log(333)
    },

    async test () {
      const res = await axios.get('http://localhost:9000/api/user/getAll')
      return res
    },


    async test2 () {
      const res = await axios.get('http://localhost:9000/api/user/count')
      return res
    },
}

例二
完善上面的黄家驹拿吉他并唱歌的代码

data() {
	return {
		data1: {},
		data2: {}
	}
}
created() {
	this.initData()
},
  methods: {
    // 模拟后端请求1
    fun1(str) {
      const p = new Promise(function(resolve,reject){
        // 模仿异步请求
        setTimeout(() => {
          if (str === "黄家驹") {
            const res = {
              code: 200,
              data: str + "拿到了吉他"
            }
            resolve(res)
          }else {
            const err = {
              code: 500,
              data: "黄家驹没有拿到吉他"
            }
            reject(err)
          }
        }, 1000)
      })
      return p
    },

    // 模拟后端请求1
    fun2(str) {
      const p = new Promise(function(resolve,reject){
        setTimeout(() => {
          if (str === "黄家驹拿到了吉他") {
            const res = {
              code: 200,
              data: str + ",并唱了首海阔天空"
            }
            resolve(res)
          }else {
            const err = {
              code: 500,
              data: "黄家驹拿到了一把坏吉他,无法演唱"
            }
            reject(err)
          }
        }, 2000)
      })
      return p
    },

	// async + await
    async getGuitar(str) {
      this.data1 = await this.fun1(str)
    },

    // async + await
    async sing(str) {
      this.data2 = await this.fun2(str)
    },
	
    // 同步执行
    async initData() {
      await this.getGuitar("黄家驹")
      await this.sing(this.data1.data)
      console.log(this.data2)
      // 只有当上面的异步任务执行完之后,下面的同步任务才会执行,也保证了代码的顺序执行
      console.log("haha")
    },
}
  • 31
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值