异步编程的方式

文章介绍了JavaScript中由于单线程特性导致的假死问题,以及为解决此问题而引入的异步编程方法,包括回调函数、Promise和Generator。Promise通过链式调用改善了回调地狱,而Generator和async/await进一步简化了异步代码,使其更接近同步风格。最后,文章展示了如何使用async/await重写异步操作,以提高代码可读性和维护性。
摘要由CSDN通过智能技术生成

为什么使用异步编程?

JS在浏览器中的运行是单线程,当前面的任务没有完成的时候,后面的任务会一直在等待,这也造成了浏览器的“假死”现象。为了避免这一现象的发生,浏览器采用异步的方式来解决这一问题。

那么异步编程的方式有哪些呢?

回调函数

最早实现异步编程的方式就是回调函数,例如定时器:

function callback1()
    {
      setTimeout(()=>{
        console.log("callback1");
      },1000);
    }
console.log(1);
callback1();

回调函数实现起来很简单,但是假如我有一个场景,这个场景是我要通过ajax请求去获取到用户数据,再根据用户数据获取到订单数据,最后根据订单数据来获取到商品数据(此处的Ajax请求利用定时器来进行模拟),我们可能会写出下面的代码:

function getMessage()
    {
      setTimeout(()=>{
        let data="用户数据";
        console.log(data);
        setTimeout(()=>{
          let data="订单数据";
          console.log(data);
          setTimeout(()=>{
            let data="商品数据";
            console.log(data);
          },3000);
        },2000);
      },3000);
    }

    getMessage();

我们可以看到我们写出来的代码,比如getMessage会产生回调函数嵌套着回调函数,这种现象我们称它为回调地狱。回调地狱不利于代码的阅读和维护。那我们应该如何对他进行改进呢?

Promise

Promise解决了回调地狱的问题,采用链式的写法进行操作。将上面的代码改写为:

let p=new Promise((resolve,reject)=>{
      setTimeout(()=>{
        let data="用户数据";
        resolve(data);
      },3000);
    }).then((res)=>{
      console.log(res);
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{
          let data="订单数据";
          resolve(data);
        },3000)
      })}).then((res)=>{
        console.log(res);
        return new Promise((resolve,reject)=>{
          setTimeout(()=>{
          let data="商品数据";
          resolve(data);
          },3000)
        })
    }).then((res)=>{
      console.log(res);
    });

当有大量的then操作时,其实也不利于代码的阅读与维护。

Generator

async/await能够解决Promise的链式调用带来的代码阅读困难的问题,async/await的原理是在Generator基础之上。在理解Generator之前,先弄清楚一个概念:协程

协程

协程可以理解为“协作的线程”。一个线程可以包含多个协程,但一个线程只能运行一个协程。它的运行流程大概是:A协程开始执行,协程A执行到一半,进入暂停,执行权交给协程B,过了一段时间,协程B交换执行权;最后,协程A恢复执行。

Generator简介

在一般的函数中,我们调用某个函数,这个函数会一直执行完,函数中途不能暂停。Generator也是一个函数,它在function与函数名间插入一个*号,这个函数可以暂停,暂停的标志为yield。yield就是把协程的执行权交出外部去,主线程获得协程的执行权之后执行主线程中的任务。如果想恢复函数的执行权则需要执行生成器的next()函数。
Generator函数的示例代码:

function * bar()
{
      console.log('step 1');
      yield 1;
      console.log('step 2');
      yield 2;
      console.log('step 3');
      yield 3;
}
const gen=bar();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

const gen=bar()并不能执行生成器的函数,只是获得生成器的一个内部指针,它返回的是一个迭代器。如果想执行bar()函数,则需要通过next()函数获得执行权,gen.next().value返回的是一个对象:{value:yield表达式后返回的值;done:true(还可以继续迭代)},获得执行权后如果遇到yield也会将函数的执行权交出去,这就是协程原理。如果还想继续执行bar()函数,则需要通过next()函数拿回执行权。
通过Generator来改写上述获取数据的代码:

function getUsers()
{
	setTimeout(()=>{
		let data="获取用户数据";
		iterator.next(data);
	},3000);
}
function getOrders()
{
	setTimeout(()=>{
		let data="获取订单数据";
		iterator.next(data);
	},3000);
}
function getGoods()
{
	setTimeout(()=>{
		let data="获取商品数据";
		iterator.next(data);
	},3000);
}
function * gen()
{
	let users=yield getUsers();
	console.log(users);
	let orders=yield getOrders();
	console.log(orders);
	let goods=yield getGoods();
	console.log(goods);
}
let iterator=gen();
iterator.next();	

以上的代码更像同步执行的代码。

async/await

Generator生成器需要我们手动调用next()函数来拿回执行权继续执行函数里的内容,aysnc/await则内置执行器,不需要我们去调用。首先,先介绍一下async/await的语法。
async用来修饰一个函数,这个函数返回一个Promise对象,await修饰一个表达式,这个表达式一般返回一个Promise对象,await返回的是Promise成功的值。
await做了两件事,第一件事就是创建一个Promise,第二件事就是将主线程的执行权返回给父协程,相当于yield,并且返回创建的Promise的对象。
当Promise对象的状态发生改变的时候,父协程将执行权交还给函数(这就是内置的夺回执行权),继续执行async函数中await语句后的代码。使用async/await的代码改写如下:

function getUsers(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("获取用户的数据");
    })
  },3000)
}

function getOrders()
{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("获取订单的数据");
    },3000)
  })
}

function getGoods()
{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("获取商品的数据");
    },3000)
  })
}

async function getMessages()
{
    let user=await getUsers();
    console.log(user);
    let orders=await getOrders();
    console.log(orders);
    let goods=await getGoods();
    console.log(goods);
}

getMessages();

在这里插入图片描述
异步编程的发展史:回调函数->Promise->Generator->async/await

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值