es6之异步编程

定义Promise对象

为了解决地狱式的回调,可以使用 Promise 对象,且代码更优雅,由于 Promise 对象是一个构造函数,因此,必须通过实例化来生成,它的定义格式如下代码:

let p = new Promise(function (resolve, reject) {
  // 此处做一个异步的事情
});

在定义格式的代码中,需要说明的几个问题:

  • 在实例化中,参数为函数,函数中又有两个用于回调的函数。
  • 两个回调函数中,resolve 为异步执行成功时的回调,其参数可以传递执行的结果。
  • reject 为异步执行失败时的回调,其参数可以传递失败的错误信息。

使用 resolve 和 reject 方法传递出去的参数被谁接收到了,是以何种方式接收的?接下来说下 then 方法。

Promise对象的then方法

Promise 对象实例化后,可以调用 then 方法获取两个回调函数中的传参值,该方法接收两个回调函数作为参数,第一个参数是必选参数,表示异步成功后执行的 resolve 回调函数,第二个参数是可选参数,表示异步失败后执行的 reject 回调函数,它的调用格式如下:

p.then(
  function (v) {},
  function (e) {}
);

其中参数 v 值表示 resolve 回调函数中的参数值,e 值表示 reject 回调函数中的参数值,如下列代码所示:

let n = 6;
let p2 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    if (n > 5) {
      resolve(n);
    } else {
      reject("必须大于5");
    }
  });
});
p2.then(
  function (v) {
    console.log(v);
  },
  function (e) {
    console.log(e);
  }
);
// 执行代码后,由于 n 值大于 5 ,因此,在控制台中输出数字 6 。

此外,一个 then 方法被执行后,如果仍然返回一个 Promise 对象,则可以继续再执行 then 方法,形成链式写法效果,代码如下所示:

p1.then(function (v) {
  return p1;
}).then(function (v) {
  return p1;
});

Promise对象中的方法

Promise.all方法

日常开发过程中,往往会遇到这种问题,当首次加载某个页面时,由于数据庞大需要分别同时发送多个异步请求向服务器获取数据,最终所有数据返回之后做下一步操作(如“隐藏页面的加载 loading 动画”)。由于很难捕获这些异步请求全部成功的时机,导致这个需求实现起来相当困难。难道就没有解决办法了吗?🤔 这时使用 Promise.all 方法就可以解决这种问题。
使用格式
Promise.all 方法中的参数是一个数组,数组中的每个元素是实例化后的 Promise 对象,格式如下代码:

Promise.all([p1,p2,p3,...]).then(res=>{
  // 所有请求成功后的操作步骤
},error=>{
  // 某一个请求失败后的操作步骤
});

实践应用
下列通过一个实战来演示 Promise.all 方法的使用过程,功能说明:

  1. 定义一个函数 p1,返回一个 Promise 对象,在返回过程中,执行一个延时操作,定义一个参数 n ,如果参数 n 大于 0
    ,则返回该数据,否则,则返回 “ 不能小于 0“ 的字符信息。
  2. 调用 Promise.all 方法,使用不同的参数,调用三次 p1 函数,当全部执行成功或有一个执行失败后,分别查看控制台的输出信息。

为了实现这个功能,首先,打开我们的线上环境,新建一个 index3.html 文件,再使用快捷键方式生成模版,并在 body 元素中添加 script 元素,如下代码所示:

function p1(n) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      if (n > 0) {
        resolve(n);
      } else {
        reject("不能小于0");
      }
    }, 1000);
  });
}

先传入三个执行成功的任务,在新建页面中的 script 元素中添加如下代码:

Promise.all([p1(5), p1(6), p1(7)]).then(
  function (v) {
    console.log(v);
  },
  function (e) {
    console.log(e);
  }
);

上述代码执行后的效果如下图所示:
请添加图片描述
从上述效果可以看出,如全部任务执行成功,则将各个执行结果保存在数组中,可以通过 then 方法中的成功回调函数返回。

此外,传入一个执行失败的任务,二个执行成功的任务,在新建页面中的 script 元素中再添加如下代码,

Promise.all([p1(5), p1(-2), p1(7)]).then(
  function (v) {
    console.log(v);
  },
  function (e) {
    console.log(e);
  }
);

上述代码执行后的效果如下图所示:请添加图片描述
从上述效果可以看出,如有一个任务执行失败,则通过 then 方法中的失败回调函数返回错误信息。

Promise.race方法

与 Promise.all 方法不同,Promise.race 方法是多个 Promise 实例化对象在比赛, 执行最快的那个任务的结果,将返回给 then 方法中的对应回调函数中,通过这种方式,可以检测页面中某个请求是否超时,并输出相关的提示信息。
使用格式
与 Promise.all 方法一样,Promise.race 中的参数也是一个数组,每个元素也是实例化后的 Promise 对象,格式如下代码:

Promise.race([p1,p2,p3,...]).then(
    function(v){
      //获取最快任务成功时的返回值
  },
  function(){
      //获取最快任务失败时的返回值
  }
)

实战应用
下列通过一个实战来演示 Promise.race 方法的使用过程,功能说明:

  1. 定义一个模拟异步请求的函数,返回一个 Promise 对象,在返回过程中,执行一个延时 3 秒的操作,请求成功后,则返回一个”请求成功“
    的字样。
  2. 再定义一个超时请求的函数,返回一个 Promise 对象,在返回过程中,执行一个延时 5 秒的操作,如果超过 5
    秒,则返回一个”请求超时“ 的字样。
  3. 调用 Promise.race 方法,添加这 2 个 Promise 对象,当请求大于 5 秒和小于 5
    秒时,分别查看控制台的输出信息。

先定义一个延时小于 5 秒的任务,在新建页面中的 script 元素中添加如下代码:

function loadData() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve("请求成功");
    }, 3000);
  });
}

然后,定义一个延时等于5秒的任务:

function timeOut() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject("请求超时");
    }, 5000);
  });
}

接下来调用 Promise.race() 方法,获取并在显示执行最快任务的返回内容,在新建页面中的 script 元素中添加如下代码:

Promise.race([loadData(), timeOut()]).then(
  function (d) {
    console.log(d);
  },
  function (e) {
    console.log(e);
  }
);

上述代码执行后的效果如下图所示:
请添加图片描述
由于 loadData 函数的延时时间小于请求超时的延时时间,因此,该任务执行最快,所以在控制台显示 ”请求成功“ 的信息。

如果将 loadData 函数的延时时间修改为 6 秒,即将 loadData 函数的代码修改为如下代码:

function loadData() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve("请求成功");
    }, 6000);
  });
}

其他代码不变,页面执行后的效果如下图所示:
请添加图片描述
由于 timeOut 函数的延时时间小于请求超时的延时时间,因此,该任务执行最快,所以在控制台显示 ”请求超时“ 的信息。

async关键字

async 英文单词的意思是异步,虽然它是 ES7 中新增加的一个关键字,但它的本质是一种语法糖写法(语法糖是一种简化后的代码写化,它能方便程序员的代码开发),async 通常写在一个函数的前面,表示这是一个异步请求的函数,将返回一个 Promise 对象,并可以通过 then 方法取到函数中的返回值,下面通过一个简单示例来说明它的使用。
实例:

async function fn() {
  return "12345";
}
fn().then((val) => {
  console.log(val);
});

在上述代码中,定义一个名称为 fn 的函数,但由于在函数前添加了关键字 async ,使这个函数将返回一个 Promise 对象,因此,函数执行后,可以直接调用 then 方法;同时,fn 函数中的返回值,就是 then 方法中,执行成功回调函数时的参数值,因此,执行上述代码后,将在页面的控制台输出 “12345” 字符,效果如下所示:请添加图片描述
通过上述示例,我们明确以下两点:

  1. 使用 async 关键字定义的函数,将会返回一个 Promise 对象。
  2. 函数中有返回值,则相当于执行了 Promise.resolve(返回值) 函数,没有返回值,则相当于执行了
    Promise.resolve() 函数。

虽然 async 关键字简化了我们之前实现异步请求中返回 Promise 实例对象的那一步,直接返回了一个 Promise 对象,但是仍然需要在 then 方法中处理异步获取到的数据。有没有什么办法可以继续优化呢?比如省去 then 方法的调用,让异步操作写起来更像同步操作那么简洁明了?答案就是—— await ,接下来我们来介绍下它的用法。

await关键字

await 可以理解为 async wait 的简写,表示等待异步执行完成,await 必须在 async 定义的函数中,不能单独使用,await 后可以返回任意的表达式,如果是正常内容,则直接执行,如果是异步请求,必须等待请求完成后,才会执行下面的代码,来看下列代码。

// 函数 p 返回的是一个 Promise 对象,在对象中,延时 2 秒,执行成功回调函数,相当于模拟一次异步请求
function p(v) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      // 在 p 函数执行时,将函数的实参值 v ,作为执行成功回调函数的返回值。
      resolve(v);
    }, 2000);
  });
}

// 一个用于正常输出内容的函数
function log() {
  console.log("2.正在操作");
}

async function fn() {
  console.log("1.开始");
  await log();
  let p1 = await p("3.异步请求");
  console.log(p1);
  console.log("4.结束");
}
fn();

执行上述代码后,页面在控制台输出的效果如下所示:
请添加图片描述
根据页面效果,源代码解析如下:

  • fn 函数执行后,首先,会按照代码执行流程,先输出“1.开始”。
  • 其次,对于没有异步请求的内容,在 await 后面都将会正常输出,因此,再输出“2.正在操作”。
  • 如果 await 后面是异步请求,那么,必须等待请求完成并获取结果后,才会向下执行。
  • 根据上述分析,由于 方法 p 是一个异步请求,因此,必须等待它执行完成后,并将返回值赋给变量 p1,再执行向下代码。
  • 所以,最后的执行顺序是,先输出 “3.异步请求”,再输出 “4.结束”,在 async 函数中的执行顺序,如下图所示。
    请添加图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT小崽子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值