promise

promise的核心:在合适的时机执行 resolve 或者 reject

浏览器执行的特点:先吧所有的同步执行完,才会执行异步

复杂的概念先不讲,我们先简单粗暴地把Promise用一下,有个直观感受。那么第一个问题来了,Promise是什么玩意呢?是一个类?对象?数组?函数? 别猜了,直接打印出来看看吧,console.dir(Promise)。
![[1280X1280 (15).PNG]]
这么一看就明白了,Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。这么说用Promise new出来的对象肯定就有then、catch方法。

回顾

异步:操纵之间没啥关系,可以同时有多个操作

同步:操作之间是相关的,同时只能做一件事

特点

异步的优势明显,可以同时完成多个操作,操作之间不互相干扰,这是她的优点,但是他也有缺点,会让代码变得更复杂 同步虽然她的缺点那么大,但是代码简单

回调地狱

需求:现在又6个div,我想给每个div都添加一个移动的动画,并且先执行第一个,再指定第二个,再执行第三个,以此类推;

这样就可以使用jquery中的animate

HTML
<style>
  .scene {
    display: flex;
    justify-content: space-around;
    width: 1400px;
    margin: 300px auto;
    border: 1px solid #000;
  }
  .scene p {
    width: 100px;
    height: 100px;
    border: 1px solid #000;
  }
  .scene p:nth-child(1) {
    background-color: #f00;
  }
  .scene p:nth-child(2) {
    background-color: green;
  }
  .scene p:nth-child(3) {
    background-color: blue;
  }
  .scene p:nth-child(4) {
    background-color: yellow;
  }
  .scene p:nth-child(5) {
    background-color: pink;
  }
  .scene p:nth-child(6) {
    background-color: purple;
  }
</style>
<div class="scene">
  <p>1</p>
  <p>2</p>
  <p>3</p>
  <p>4</p>
  <p>5</p>
  <p>6</p>
</div>
// 需求:第一个萝卜蹲完,第二个萝卜蹲,第二个萝卜蹲完,第三个萝卜蹲 。。。
// 回调地狱 : 回调函数多层嵌套
$(".scene p").eq(0).animate({
  marginTop: '-200px'
}, 1000, function () {  
$(".scene p").eq(1).animate({
    marginTop: '200px'
  }, 1000, function () {
    $(".scene p").eq(2).animate({
      width: '200px'
    }, 1000, function () {
      
$(".scene p").eq(3).animate({
        height: '200px'
      }, 1000, function () {
        $(".scene p").eq(4).animate({
          marginLeft: '-200px'
        }, 1000, function () {
          $(".scene p").eq(5).animate({
            marginTop: '200px'
          },1000)
        })
      })
    })
  })
})

可以实现效果,但是需要嵌套很多层的回调函数,如果需求量再大,回调层级会更多,我们把这种多次的回调称之为 “回调地狱”

正常情况下异步操作中,经常是使用ajax来进行请求[ajax的回调地狱]

// SPA: 单页面应用,整个应用只有一个html文件,所有的数据都是按需加载(ajax)
// 1. ajax请求:影片列表 : 《夺冠》 《隐秘的角落》 《少年的你》 
// 2. ajax请求: 获取影片的详细信息 周冬雨   (api/movieInfo.php?id=1  ===>  actorId:24)
// 3. ajax请求:周冬雨的信息 (api/actor.php?actorId=24  ==>  schoolId: 30)
// 4. ajax请求: 周冬雨毕业学校的信息  (api/school.php?scollId=30)
// 只是模拟过程,代码不能运行
function render(data){
}
$.ajax({
  url: 'api/movieInfo.php',
  type: 'get',
  data: {
    id: 1
  },
  success(data){
    // 主演的id
    let {actorId} = data;
    $$.ajax({
      url: 'api/actor.php',
      type: 'get',
      data: {
        actorId
      },
      success(data){
        // 演员的学校信息
        let {schoolId} = data;
        $.ajax({
          url: 'api/school.php',
          type: 'get',
          data: {
            schoolId
          },
          success(data){
            // 得到学校的信息
            render(data)
          }
        })
      }
    })
  }
})

想要实现相同的功能,那么就需要用类似同步的方式实现异步的效果;

就可以使用promise用来解决 他可以把异步的代码用同步的方式来实现

语法结构: 
new Promise(function(resolve,reject){ 
    // 异步操作代码 
    resolve:成功的回调 
    reject:失败的回调
}) 

promise用来解决回调地狱的问题,把异步的代码用同步的方式来实现

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

Promise的三种状态

pending: 初始状态,既不是成功,也不是失败状态。

fulfilled: 意味着操作成功完成。

rejected: 意味着操作失败。

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

Resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

案例:使用promise发送ajax请求

  1. 使用Promise请求arr.txt数据
let p=new Promise(function(resolve,reject){
  $.ajax({
    url:'arr.txt',// 路径
    dataType:'json',// 让返回的数据变成json
    // 请求成功,接收数据,这个格式是不是就是之前说的json方法的简写
    success(arr){
      // 以前成功,直接输出数据;现在不用,成功之后直接调用resolve方法
      // 成功的时候调用resolve,并返回数据
      resolve(arr);
    },
    error(err){
      // 错误了,调用reject,并且接收一下返回来的错误信息err
      reject(err);
    }
  })
})
  1. 接收请求回来的数据
p.then(function () {
  // 说白了这个就是resolve
  alert('ok');
}, function () {
  // 这个是reject
  alert('失败');
});
  1. 需要发送多个ajax请求,就需要创建多个Promise
JavaScript
let p1 = new Promise(function (resolve, reject) {
  $.ajax({
    url: 'arr.txt',// 路径
    dataType: 'json',// 让返回的数据变成json
    success(arr) {
      resolve(arr);
    },
    error(err) {
      reject(err);
    }
  })
})
let p2 = new Promise(function (resolve, reject) {
  $.ajax({
    url: 'arr.txt',// 路径
    dataType: 'json',// 让返回的数据变成json
    success(arr) {
      resolve(arr);
    },
    error(err) {
      reject(err);
    }
  })
})
// 接收所有的请求结果
Promise.all([p1,p2]).then(function () {
  // 说白了这个就是resolve
  alert('全都成功了');
}, function () {
  // 这个是reject
  alert('至少有一个失败了');
});

这时候可以接收两个请求回来的结果

  1. 成功之后,分别接收里面的参数值
Promise.all([
  p,p1
  ]).then(function (res) {
    // 里面是数组格式,返回的是所有请求回来的结果
    console.log(res);
    // 分解返回的结果===用解构赋值
    let[res1,res2]=res;
    console.log(res1);
    console.log(res2);
  },function () {
    alert('至少有一个失败了');
  }
);
  1. 将两个相同的Peromise放在一个函数中,并将请求的路径传递进去
function createPromise(url){
  return new Promise(function (resolve, reject) {
    $.ajax({
      url,// 路径
      dataType: 'json',// 让返回的数据变成json
      success(arr) {
        resolve(arr);
      },
      error(err) {
        reject(err);
      }
    })
  })
}
// 接收所有的请求结果
Promise.all([
  createPromise('1.txt'),
  createPromise('2.txt')
]).then(function () {
  // 说白了这个就是resolve
  let[res1,res2]=res;
  console.log(res1);
  console.log(res2);
}, function () {
  // 这个是reject
  alert('失败');
});
  1. jquery中的ajax本身自带Promise支持,所以可以直接调用promise【精简版】
Promise.all([
  $.ajax({url:'arr.txt',dataType:'json'}),
  $.ajax({url:'json.txt',dataType:'json'})
  ]).then(function(res){
    // 成功的回调
    let [res1,res2]=res;
    console.log(res1,res2);
  },function(){
    // 失败的回调
    alert('失败了');
})
  1. 如果不是一个ajax请求,当前的promise是怎样执行的
function demo(){
  return new Promise(function (resolve, reject) {
    if(true){
      resolve();
    }else{
      reject()
    }
  })
}
// console.log(demo()); // 打印promise对象
demo().then(function(){
  console.log('成功');
}).catch(function(){
  console.log('失败');
})
console.log('任务完成');

promise的all方法和race方法

all:当两个异步操作都成功完成后,再执行的逻辑

race:比赛;谁快获取谁;最先得到的异步操作,即执行下面的业务逻辑

需求: ajax1和ajax2都执行完再执行task (task的业务代码依赖ajax1和ajax2的结果)
JavaScript
let flag1 = false;
let flag2 = true;
function ajax1() {
  return new Promise((resolve, reject) => {
    // 真正的业务代码
    setTimeout(function () {
      console.log('ajax1');
      if (flag1) {
        resolve('ajax1的data')
      } else {
        reject('ajax1的错误')
      }
    }, 1000)
  })
}
function ajax2(actorId) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      console.log('ajax2');
      if (flag2) {
        resolve('ajax2的data')
      } else {
        reject('ajax2的错误')
      }
    }, 2000)
  })
}
function task(data) {
  console.log('task', data);
}
function err(err){
  console.log(err);
}
// Promise.all(): 两个异步操作都成功完成后,再执行的逻辑
// all()和race()中的参数必须是promise实例
// Promise.all([ajax1(), ajax2()])
//     .then(task)
//     .catch(err)
// Promise.race(): 最先得到结果的异步操作执行成功,即执行下面的逻辑
// Promise.race([ajax1(), ajax2()])
//  .then(task) // 必须要得到异步的结果,否则没办法判断
总结:all和race的区别

all:100个人跑步跑步:等100个跑到终点才结束

race:只要第一个跑到终点就结束,后面的99个就不管了

使用promise解决for循环和setTimeout的问题

// for循环中嵌套定时器,输出的内容都是一样的
for(var i=0;i<5;i++){
  setTimeout(()=>{
    console.log(i)
  }, 10)   
}

解决方案

// 可以解决,但是不推荐,没有这样的使用场景
for(var i=0;i<5;i++){
  // 添加Promise
  function a(i) {
    return new Promise((resolve, reject) => {
      setTimeout(()=>{
        resolve(i)
      }, 10)
    })
  };
  a(i).then(res => {
    console.log(res)
  });
}

面试题:Promise是同步的还是异步的

结论:promise本身是同步的,但是里面的resolve, reject方法是异步的

console.log(1)
let a = new Promise((res,rej) => {
  console.log(2);
});
console.log(3);
let b = new Promise((res,rej) => {
  console.log(4);
});
console.log(5);
// 当前程序的输出顺序为:1 2 3 4 5

添加resolve和reject之后

console.log(1)
let a = new Promise((res,rej) => {
  res();
  console.log(2);
});
a.then(res=>{
  console.log('ok');
}).catch(err=>{
  console.log('err');
})
console.log(3);
let b = new Promise((res,rej) => {
  console.log(4);
});
console.log(5);
// 当前程序的输出顺序为:1 2 3 4 5 ok
  1. ES6 try-catch
    try…catch语句用来捕捉异常

throw 语句抛出一个错误

try {

tryCode – 尝试执行的代码

} catch(error) {

catchCode – 捕捉到错误的代码时触发

} finally {

finallyCode – 无论走 try/catch 都要执行这里的代码

}
// 案例1
try {
  alert("1");
} catch (error) {
  alert(error);
} finally {
  alert("Hello, 郑州");
}
// 案例2
var a = 3;
try {
  if (a < 5) {
    alert("+++++++");
    let a = 10000;
    // throw 的值会传递给 catch
    throw a;
  }
  if (a < 10 && a >= 5) throw "这个数小于10";
  if (a < 15 && a >= 10) throw "这个数小于15";
} catch (msg) {
  alert(msg);
}

使用场景:转换对象的格式[添加try - catch]

try {
  let data = '{"name":"XX";"age":100}'
  let newData = JSON.parse(data)
  console.log(newData.name);
} catch (err) {
  console.log('json格式不正确');
}
try catch缺点:
  1. try catch耗性能

  2. try catch捕获不到异步错误

  3. try catch可能会导致报错点更模糊

ES6模块化开发

在es6中一个文件可以默认为一个模块,模块通过export向外暴露接口,实现模块间交互等功能;主要分为两部分,导入 和导出

  1. 导出文件 关键词 export {} 后面的花括号不能少
let a = 100;
let name = "张三";
function sum(a, b) {
  return a + b;
}
// 导出 export原意是:输出,出口 
export {a, name, sum}; // 导出的是什么名字,导入的时候还必须是这个名字
  1. 导入文件,关键词 import {}
注意,必须把script改为

async和await

什么是async?

async可以理解为generate的语法糖/修饰符,async是异步的意思,用来声明一个函数是异步的。

// 普通函数
function show(){
  return "我是一个单纯的show函数";
}
show();
// 使用async变成Promise函数
async function show() {
    return "hello";
}
let result1 = show();        
console.log(result1);       //Promise {'hello'}

通过这一段代码,我们可以发现,加了async后,函数执行返回的并不是"hello",而是返回了一个Promise对象。如果在函数中 return 一个直接量, async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。

async function fun2() {
     return 'hello'
}
let result2 = fun2();       
console.log(result2); //Promise {undefined}

如果是没有return返回值,那么promise就会把undefined作为返回值来进行返回; 想要把一个普通函数变成promise函数,那么函数的返回值就是Promise中resolve的结果

什么是await?

await是等待的意思,顾名思义,它是用来等待某个动作的完成 一般来说,都认为 await 是在等待一个 async 函数完成。

不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。

因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。

注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。

function show(time,val){
     return new Promise((resolve,reject)=>{
       setTimeout(()=>{
         resolve(val)
       },time)
     })
 }
// 正常调用 异步
show(1000,'111111').then(res=>{
  console.log(res);
})
show(2000,'22222').then(res=>{
  console.log(res);
})
show(5000,'33333').then(res=>{
  console.log(res);
})

promise本身是同步的,但是then方法是异步的,这时候当执行代码的时候,会发现他们是一起执行的

使用await

function show(time,val){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve(val)
      },time)
    })
}
// 同步调用
async function test(){
  let a = await show(1000,'111111');
  console.log(a);
  let b = await show(2000,'22222')
  console.log(b);
  let c = await show(5000,'33333')
  console.log(c);
}
test();

await可以把异步操作变成同步,但是需要注意await外面必须要有一个async函数,否则不能执行

![[72fee6e3-3be8-46b0-a537-7960ad0d72fa.png]]

async/await掷骰子案例
<button>摇一摇</button>
</body>
<script>
    function getNum(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve(Math.floor(Math.random()*6+1));
            },1000)
        })
    }
    var btn = document.querySelector('button');
    btn.onclick = async function(){
        let num = await getNum();
        console.log(num);
    }
</script>
jquery的ajax使用await(当ajax请求成功,数据渲染完毕之后控制台输出123)
// ajax是promise风格的,所以可以直接使用then方法
$.ajax({
    url:'demo.txt',
    dataType:"json"
}).then(res=>{
    console.log(res);
})
console.log('123'); // 123 {name: 'jack', age: '18'}

// 给ajax添加await
async function demo(){
    let res = await $.ajax({
        url:'demo.txt',
        dataType:"json"
    });
    console.log(res);
    console.log('123');
}
demo(); // {name: 'jack', age: '18'} 123
async请求数据

需求:页面中有nav模块,banner模块,商品信息模块,要求先加载nav,再加载banner,再加载商品模块;

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script>
  function pro(url,data){
      return new Promise((resolve,reject)=>{
        $.ajax({
          url,
          data,
          dataType:'json',
          success(res){
            resolve(res)
          }
        })
      })
  }
  async function test(){
      let ask = await pro('https://cnodejs.org/api/v1/topics',{tab:'ask',limit:3});
      console.log(ask);
      let good = await pro('https://cnodejs.org/api/v1/topics',{tab:'good',limit:3});
      console.log(good);
      let job = await pro('https://cnodejs.org/api/v1/topics',{tab:'job',limit:3});
      console.log(job);
  }
  test();
  // https://cnodejs.org/api/v1/topics?tab=ask&limit=3
  // https://cnodejs.org/api/v1/topics?tab=good&limit=3
  // https://cnodejs.org/api/v1/topics?tab=job&limit=3
</script>

添加完await,就能够让我们写异步代码就像同步代码一样简单

思考:分析代码执行顺序

async function show(){
    console.log(11111);
    let two = await Promise.resolve('2222222');
    console.log(two);
    console.log(333333);
    return Promise.resolve( "我是一个单纯的show函数");
}
show().then(res=>{
    console.log(res);
})
案例:使用promise.all请求数据

fetch返回的是一个异步的请求,如果想要把请求变成同步可以使用await await: 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。但是:await只有在async中才可以使用; async:函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。

let show = async ()=>{
  let url = 'https://gorest.co.in/public/v1/users';
  // 使用try-catch补货错误信息
  try{
      let res = await Promise.all(
          [fetch(${url}/3434/),fetch(${url}/3433/),fetch(${url}/3432/)]
      );
      let jsons = res.map(response => response.json());
      let val = await Promise.all(jsons);
      val.map(value=>{
          console.log(value.data.name);
      })
  }catch(error){
      console.log(error);
  }
}
show();

练习使用promise异步请求,实现淘小说数据渲染

官网地址: https://m.lrts.me/

编写懒人听书移动端首页及跳转详情

首页数据接口地址:https://m.lrts.me/ajax/getHomePage

详情页数据接口地址:https://m.lrts.me/ajax/getBookDetail?bookId=43072454

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐