javascript异步编程的解决方案

javascript异步编程的解决方案

前言

javascript语言的执行环境是"单线程"。也就是指一次只能完成一件任务。
如果有多个任务,就必须排队,前面一个任务完成,在执行后面一个任务。

这种模式实现起来比较简单,但只要一个任务的耗时很长,后面的任务就必须排队等待造成整个页面卡在一个地方无法继续执行。

为了解决这个问题Javascript语言将任务的执行模式分成两种:同步和异步。

下面主要讲讲异步编程的解决方案:

一、回调函数

回调函数是解决异步编程最基本的方法

假如现在有两个ajax请求(ajaxA),(ajaxB)需要执行,但是ajaxB的执行需要依赖于ajaxA请求成功返回的结果,那么我们就可以将ajaxB放到ajaxA的成功的回调中执行,这样就可以解决异步编程的问题

但这种解决方案存在缺点:如果同时存在大量的请求就会造成大量回调函数相互嵌套,势必会造成回调地狱

二、Promise

promise是一种异步编程的解决方案,他将回调函数相互嵌套的模式改为了级联的方式

Promise函数的内部状态
  • pending 进行中
  • resolved 已完成
  • rejected 失败

Promise 对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获

注意事项
  1. reject方法的作用,等同于抛出错误。
  2. 如果 Promise 状态已经变成resolved,再抛出错误是无效的。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。
  3. Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
  4. then可以接收两个参数,成功和失败。 但是失败只能捕获本次的错误
  5. Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误,Promise可以“吃掉”错误
  6. catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。代码运行完catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法
Promise 对象方法
then 方法:

Promise异步函数结束之后,调用then方法

catch 方法:

当Promise 异步函数 出现错误,会调用catch方法

finally 方法:

无论成功 失败都会执行 finally 方法

Promise.all 方法

可以将多个Promise对象包装成一个新的 Promise 实例。 (并行)

  • Promise.all方法接受一个数组作为参数,参数必须都是 Promise 对象
  • 只有 参数 的状态都变成fulfilled,Promise.all 对象的结果才为fulfilled
  • Promise.all 对象可以接收每个 参数的返回值
Promise.race 方法

可以将多个Promise对象包装成一个新的 Promise 实例。

  • Promise.race方法接受一个数组作为参数,参数必须都是 Promise 对象
  • 只要参数之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
Promise.resolve 方法

将任意数据变为变为 Promise 对象,如果其已经时Promise对象,则无任何操作。

Promise的缺点
  • promise一旦新建就会立即执行,中途无法取消

  • 当处于pending状态时,无法得知当前出于那一个状态,是刚开始还是刚结束

  • 如果不设置回调函数,promise内部的错误就无法反映到外部

  • promise封装ajax时,由于promise是异步,发送请求的三部会被延后到同步代码执行完毕,并且将相应回调延迟到现有有队列的最后,大量使用会降低请求效率

  • Promise 的最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。

三、Generator函数

可以把它理解成Generator函数是一个状态机,封装了多个内部状态。

function* helloGenerator(){
    yield 'hello';
    yield 'world';
    return 'ending';
}
var hello = helloGenerator();

执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象。

接下来,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

var hello = helloGenerator();
hello.next()
// { value: 'hello', done: false }

hello.next()
// { value: 'world', done: false }

hello.next()
// { value: 'ending', done: true }

hello.next()
// { value: undefined, done: true }

由于Generator函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

遍历器对象的next方法的运行逻辑如下。

遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
如果该函数没有return语句,则返回的对象的value属性值为undefined。
需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行。

缺点

可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。即如何实现自动化的流程管理。

四、async、await

async函数就是 Generator 函数的语法糖

async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

async 的优点
  1. 内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
  2. 更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
  3. 更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
  4. 返回值是 Promise。async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。用then方法指定下一步的操作。
async函数的用法
function timeout(ms) {
 return new Promise((resolve) => {
  setTimeout(resolve, ms);
 });
}
async function asyncPrint(value, ms) {
  await console.log(1111111);
  await timeout(ms);
  await console.log(2222222);
  console.log(value)
}
asyncPrint('hello world', 50);
async 函数的实现原理

async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。


async function fn(args){
 // ...
}
// 等同于
function fn(args){
 return spawn(function*() {
  // ...
 });
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值