async 和 await 是 ES2016 新增两个关键字,它们借鉴了 ES2015 中生成器在实际开发中的应用,目的是简化 Promise api 的使用,并非是替代 Promise。
lodash的forEach和[].forEach不支持await,如果非要一边遍历一边执行await,可使用for-of

async

目的是简化在函数的返回值中对Promise的创建
async 用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置,被修饰函数的返回结果一定是 Promise 对**象**。

  1. async function test(){
  2. console.log(1);
  3. return 2; // 对应的是 resolve(2);
  4. }

等效于

  1. function test(){
  2. return new Promise((resolve, reject)=>{
  3. console.log(1);
  4. resolve(2);
  5. })
  6. }

当其返回结果是Promise

  1. async function test(){
  2. console.log(1);
  3. return new Promise((resolve, reject) =>{
  4. resolve(123)
  5. });
  6. }
  7. test().then(value =>{
  8. console.log(value)
  9. })

image.png

其失败案例

  1. async function love(){
  2. setTimeout(() => {
  3. if(Math.random() < 0.3){
  4. return true
  5. // 此处返回无效,setTimeout不支持 Promise 且此处的
  6. // 返回的结果是 箭头函数内部的返回值,并不是love的返回值
  7. }else{
  8. return false
  9. }
  10. }, 0);
  11. }

await

await关键字必须出现在async函数中
await用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。
如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行
await 会等到promise函数运行出结果
image.png
如图 await关键字之后画红圈的代码都是写在then()里面的

  1. async function test1(){
  2. console.log(1);
  3. return 2;
  4. }
  5. async function test2(){
  6. const result = await test1();
  7. console.log(result); // 这句代码相当于写在then()内
  8. }
  9. test2();

等效于

  1. function test1(){
  2. return new Promise((resolve, reject)=>{
  3. console.log(1);
  4. resolve(2);
  5. })
  6. }
  7. function test2(){
  8. return new Promise((resolve, reject)=>{
  9. test1().then(data => {
  10. const result = data;
  11. console.log(result);
  12. resolve();
  13. })
  14. })
  15. }
  16. test2();

rejected状态

  1. async function fun1() {
  2. console.log(1)
  3. throw "错误"
  4. }
  5. async function fun2() {
  6. try {
  7. const value = await fun1()
  8. console.log(value)
  9. }catch (err){
  10. console.log(err)
  11. }
  12. // 相当于
  13. // fun2().then(value =>{
  14. // console.log(value)
  15. // }, err =>{
  16. // console.log(err)
  17. // })
  18. }
  19. fun2()

如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行

  1. async function text (){
  2. const result = await 1;
  3. console.log(result)
  4. }
  5. text()
  6. // 等效于
  7. function text(){
  8. return new Promise ((resolve, reject) =>{
  9. Promise.resolve(1).then(data => {
  10. const result = data;
  11. console.log(data)
  12. resolve()
  13. })
  14. })
  15. }
  16. text()

改造计时器函数

  1. function delay(duration) {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve();
  5. }, duration);
  6. })
  7. }
  8. async function biaobai(god) {
  9. console.log(`邓哥向${god}发出了表白短信`);
  10. await delay(500);
  11. return Math.random() < 0.3;
  12. }

案例1

  1. // 辅助函数,把传进来的对象拼接成url的字符串
  2. function toData(obj) {
  3. if (obj === null) {
  4. return obj;
  5. }
  6. let arr = [];
  7. for (let i in obj) {
  8. let str = i + "=" + obj[i];
  9. arr.push(str);
  10. }
  11. return arr.join("&");
  12. }
  13. // 封装Ajax
  14. function ajax(obj) {
  15. return new Promise((resolve, reject) => {
  16. //指定提交方式的默认值
  17. obj.type = obj.type || "get";
  18. //设置是否异步,默认为true(异步)
  19. obj.async = obj.async || true;
  20. //设置数据的默认值
  21. obj.data = obj.data || null;
  22. // 根据不同的浏览器创建XHR对象
  23. let xhr = null;
  24. if (window.XMLHttpRequest) {
  25. // 非IE浏览器
  26. xhr = new XMLHttpRequest();
  27. } else {
  28. // IE浏览器
  29. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  30. }
  31. // 区分get和post,发送HTTP请求
  32. if (obj.type === "post") {
  33. xhr.open(obj.type, obj.url, obj.async);
  34. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  35. let data = toData(obj.data);
  36. xhr.send(data);
  37. } else {
  38. let url = obj.url + "?" + toData(obj.data);
  39. xhr.open(obj.type, url, obj.async);
  40. xhr.send();
  41. }
  42. // 接收返回过来的数据
  43. xhr.onreadystatechange = function() {
  44. if (xhr.readyState === 4) {
  45. if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
  46. resolve(JSON.parse(xhr.responseText))
  47. } else {
  48. reject(xhr.status)
  49. }
  50. }
  51. }
  52. })
  53. }
  54. //获取李华所在班级的老师的信息
  55. //1. 获取李华的班级id Promise
  56. //2. 根据班级id获取李华所在班级的老师id Promise
  57. //3. 根据老师的id查询老师信息 Promise
  58. async function getTeacher() {
  59. const stus = await ajax({
  60. url: "./data/students.json"
  61. })
  62. let cid;
  63. for (let i = 0; i < stus.length; i++) {
  64. if (stus[i].name === "李华") {
  65. cid = stus[i].classId;
  66. }
  67. }
  68. const cls = await ajax({
  69. url: "./data/classes.json"
  70. })
  71. let tid;
  72. for (let i = 0; i < cls.length; i++) {
  73. if (cls[i].id === cid) {
  74. tid = cls[i].teacherId;
  75. }
  76. }
  77. const ts = await ajax({
  78. url: "./data/teachers.json"
  79. })
  80. for (let i = 0; i < ts.length; i++) {
  81. const element = ts[i];
  82. if (element.id === tid) {
  83. console.log(element);
  84. }
  85. }
  86. }
  87. getTeacher();

与其对比的代码

  1. // 辅助函数,把传进来的对象拼接成url的字符串
  2. function toData(obj) {
  3. if (obj === null) {
  4. return obj;
  5. }
  6. let arr = [];
  7. for (let i in obj) {
  8. let str = i + "=" + obj[i];
  9. arr.push(str);
  10. }
  11. return arr.join("&");
  12. }
  13. // 封装Ajax
  14. function ajax(obj) {
  15. return new Promise((resolve, reject) => {
  16. //指定提交方式的默认值
  17. obj.type = obj.type || "get";
  18. //设置是否异步,默认为true(异步)
  19. obj.async = obj.async || true;
  20. //设置数据的默认值
  21. obj.data = obj.data || null;
  22. // 根据不同的浏览器创建XHR对象
  23. let xhr = null;
  24. if (window.XMLHttpRequest) {
  25. // 非IE浏览器
  26. xhr = new XMLHttpRequest();
  27. } else {
  28. // IE浏览器
  29. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  30. }
  31. // 区分get和post,发送HTTP请求
  32. if (obj.type === "post") {
  33. xhr.open(obj.type, obj.url, obj.async);
  34. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  35. let data = toData(obj.data);
  36. xhr.send(data);
  37. } else {
  38. let url = obj.url + "?" + toData(obj.data);
  39. xhr.open(obj.type, url, obj.async);
  40. xhr.send();
  41. }
  42. // 接收返回过来的数据
  43. xhr.onreadystatechange = function () {
  44. if (xhr.readyState === 4) {
  45. if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
  46. resolve(JSON.parse(xhr.responseText))
  47. } else {
  48. reject(xhr.status)
  49. }
  50. }
  51. }
  52. })
  53. }
  54. //获取李华所在班级的老师的信息
  55. //1. 获取李华的班级id Promise
  56. //2. 根据班级id获取李华所在班级的老师id Promise
  57. //3. 根据老师的id查询老师信息 Promise
  58. const pro = ajax({
  59. url: './data/students.json'
  60. })
  61. //对比代码从这开始
  62. pro.then(value => {
  63. for (let i = 0; i < value.length; i++) {
  64. if (value[i].name === '李华') {
  65. return value[i].classId
  66. }
  67. }
  68. }).then(cid => {
  69. return ajax({
  70. url: "./data/classes.json?id=" + cid,
  71. }).then(value => {
  72. for (let i = 0; i < value.length; i++) {
  73. if (value[i].id === cid) {
  74. return value[i].teacherId
  75. }
  76. }
  77. })
  78. }).then(tid => {
  79. return ajax({
  80. url: "./data/teachers.json?id=" + tid,
  81. }).then(value =>{
  82. for (let i = 0; i < value.length; i++) {
  83. if(value[i].id === tid){
  84. console.log(value[i])
  85. }
  86. }
  87. })
  88. })

案例2

  1. /*
  2. 邓哥心中有三个女神
  3. 有一天,邓哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止
  4. 用代码模拟上面的场景
  5. */
  6. function love(index){
  7. return new Promise((resolve, reject) =>{
  8. console.log(`邓哥向${index}发出了表白短信`)
  9. if(Math.random() < 0.3){
  10. resolve(true)
  11. }else{
  12. resolve(false)
  13. }
  14. })
  15. }
  16. (async () => {
  17. for(let i = 0 ; i < 10 ; i++){
  18. const value = await love(i) // 当前循环等待的Promise没有resolve,下一次循环不运行
  19. if(value){
  20. console.log(` ${i}回复信息啦`)
  21. break;
  22. }else{
  23. console.log(`${i}没有回复信息`)
  24. }
  25. }
  26. })()

与其做对比的代码

  1. /*
  2. 邓哥心中有三个女神
  3. 有一天,邓哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止
  4. 用代码模拟上面的场景
  5. */
  6. function love(girl) {
  7. return new Promise((resolve, reject) => {
  8. console.log(`邓哥向女神【${girl}】发出短信`)
  9. setTimeout(() => {
  10. if (Math.random() < 0.5) {
  11. resolve(true)
  12. } else {
  13. resolve(false)
  14. }
  15. }, 0);
  16. })
  17. }
  18. let por;
  19. let isTrue = false;
  20. for (let i = 0; i < 10; i++) {
  21. if(i == 0){
  22. por = love(i)
  23. }
  24. por = por.then(value =>{
  25. if(value ){
  26. console.log(`女神【${i}】回复啦短信`)
  27. if(isTrue){
  28. console.log(`女神【${i}】Sorry 发错人啦`)
  29. }else{
  30. isTrue = true;
  31. console.log(`女神【${i}】是你就是你`)
  32. }
  33. }else{
  34. console.log(`女神【${i}】没有回复啦短信`)
  35. }
  36. if(i < 9){
  37. return love(i + 1);
  38. }
  39. })
  40. }