【Promise】入门-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题

预备知识

1.1 实例对象与函数对象

  • 实例对象:new 函数产生的对象,称为实例对象,简称为对象
  • 函数对象:将函数作为对象使用时,称为函数对象

function Fn() { // Fn只能称为函数
}
const fn = new Fn() // Fn只有new过的才可以称为构造函数
//fn称为实例对象
console.log(Fn.prototype)// Fn作为对象使用时,才可以称为函数对象
Fn.bind({}) //Fn作为函数对象使用
$('#test') // $作为函数使用
$.get('/test') // $作为函数对象使用

()左边是函数,点左边是对象(函数对象、实例对象)

1.2 两种类型的回调函数

1. 同步回调

立即执行,完全执行完了才结束,不会放入回调队列中

数组遍历相关的回调 / Promise的executor函数

const arr = [1, 3, 5];
arr.forEach(item => { // 遍历回调,同步回调,不会放入队列,一上来就要执行
  console.log(item);
})
console.log('forEach()之后')

2. 异步回调

不会立即执行,会放入回调队列中将来执行

定时器回调 / ajax回调 / Promise成功或失败的回调

// 定时器回调
setTimeout(() => { // 异步回调,会放入队列中将来执行
  console.log('timeout callback()')
}, 0)
console.log('setTimeout()之后')
// Promise 成功或失败的回调
new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {console.log('value', value)},
  reason => {console.log('reason', reason)}
)
console.log('----')
// ----
// value 1

js 引擎先把初始化的同步代码都执行完成后,才执行回调队列中的代码

1.3 JS中的异常error处理

1. 错误的类型

Error:所有错误的父类型

ReferenceError:引用的变量不存在

console.log(a) // ReferenceError:a is not defined

TypeError:数据类型不正确

let b
console.log(b.xxx)
// TypeError:Cannot read property 'xxx' of undefined

let c = {}
c.xxx()
// TypeError:c.xxx is not a function

RangeError:数据值不在其所允许的范围内

function fn() {
  fn()
}
fn()
// RangeError:Maximum call stack size exceeded

SyntaxError:语法错误

const c = """"
// SyntaxError:Unexpected string

2. 错误处理(捕获与抛出)

抛出错误:throw error

function something() {
  if (Date.now()%2===1) {
    console.log('当前时间为奇数,可以执行任务')
  } else { //如果时间为偶数抛出异常,由调用来处理
    throw new Error('当前时间为偶数,无法执行任务')
  }
}

捕获错误:try ... catch

// 捕获处理异常
try {
  something()
} catch (error) {
  alert(error.message)
}
  1. 错误对象
    -massage 属性:错误相关信息
    -stack 属性:函数调用栈记录信息
try {
  let d
  console.log(d.xxx)
} catch (error) {
  console.log(error.message)
  console.log(error.stack)
}
console.log('出错之后')
// Cannot read property 'xxx' of undefined
// TypeError:Cannot read property 'xxx' of undefined
// 出错之后

因为错误被捕获处理了,后面的代码才能运行下去,打印出‘出错之后’

一.什么是Promise:

Promise 是在 js 中进行异步编程的新解决方案。(以前旧的方案是单纯使用回调函数)
从语法来说,promise是一个构造函数。
从功能来说,promise对象用来封装一个异步操作,并且可以获得成功或失败的返回值。
JS中的常见的异步操作:定时器,AJAX中一般也是异步操作(也可以同步),回调函数可以理解为异步(不是严谨的异步操作)…等。
剩下的都是同步处理

二.为啥使用Promise:

promise使用回调函数更灵活。旧的回调函数必须在启动异步任务前指定。
promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至能在异步任务结束后指定多个)
promise支持链式调用,可以解决回调地狱问题。(回调地狱就是多层回调函数嵌套使用,就是套娃,这样就不利于阅读和异常处理。)

三. promise初体验:

效果:点击一个按钮,有30%概率显示中奖。
实现: 点击按钮后得到一个1到100间的随机数,小于等于30输出中奖,否则输出没中。期间用定时器模拟异步操作,在定时器里执行判断。
(1)基础的写法:

<button id="btn">click</button>
    <script>
        var btn = document.querySelector("#btn");
    // 该函数返回一个两数之间的随机整数,包括两个数在内
    function getRandomIntInclusive(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值 
    }
    // 点击事件
     btn.addEventListener('click',function(){
         // 1秒的定时器,模拟异步操作
         setTimeout(() => {
             // 得到1到100间的一个数
             let n = getRandomIntInclusive(1, 100);
             if (n <= 30) {
                 alert('中奖喽');
             } else {
                 alert('没中奖');
             }
         }, 1000);
     })
    </script>
(2)promise写法,在promise里封装一个异步操作。
    <script>
        var btn = document.querySelector("#btn");
    // 得到一个两数之间的随机整数,包括两个数在内
    function getRandomIntInclusive(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值 
    }
    // 点击事件
     btn.addEventListener('click',function(){
         // 下面是promise ,resolve表示解决,reject表示拒绝 ,都是函数类型数据。
         const p = new Promise((resolve,reject) => {
             setTimeout(()=>{
                  let n = getRandomIntInclusive(1, 100);
                  if(n<=30){
                      resolve(); //将promise状态设为 “成功”
                  }else{
                      reject(); //将promise状态设为 “失败”
                  }
             },1000)
         });
         // 调用then方法
         p.then(()=>{
             // “成功”执行这步
            alert('中奖喽');
         },()=>{
             // “失败”执行这步
            alert('没中奖');
         })
     })

假如要在上面输出结果后加上选中的号码
因为.then()里并不能直接获得 n,所以在resolve与reject里把n当结果值返回。

 // 点击事件
     btn.addEventListener('click',function(){
         // 下面是promise ,resolve表示解决,reject表示拒绝 ,都是函数类型数据。
         const p = new Promise((resolve,reject) => {
             setTimeout(()=>{
                  let n = getRandomIntInclusive(1, 100);
                  if(n<=30){
                      resolve(n); //将promise状态设为 “成功”,并把结果值n返回
                  }else{
                      reject(n); //将promise状态设为 “失败”,并把结果值n返回
                  }
             },1000)
         });
         // 调用then方法
         // value为值的意思,reason为理由的意思,都是形参
         p.then((value)=>{
             // “成功”执行
            alert('中奖喽'+value);
         },(reason)=>{
             // “失败”执行
            alert('没中奖'+reason);
         })
     })

四:promise体验ajax请求:

效果:点击按钮获得一句名言在控制台输出。
基础写法:

 <script>
        var btn = document.querySelector("#btn");
    // 点击事件
      btn.addEventListener('click',function(){
         //创建对象
         const xhr = new XMLHttpRequest();
         //初始化
         xhr.open('GET',"http://poetry.apiopen.top/sentences");
         //发送
         xhr.send();
         //处理响应结果
         xhr.onreadystatechange = function(){
             if(xhr.readyState === 4){
                 if(xhr.status >=200 && xhr.status < 300){
                     //输出响应体
                     console.log(xhr.response);
                 }else{
                     //输出响应状态码
                     console.log(xhr.status);
                 }
             }
         }
     })
    </script>
promise封装:
 // 点击事件
     btn.addEventListener('click',function(){
         const p = new Promise((resolve,reject) => {
              //创建对象
         const xhr = new XMLHttpRequest();
             //初始化
             xhr.open('GET', "http://poetry.apiopen.top/sentences");
             //发送
             xhr.send();
             //处理响应结果
             xhr.onreadystatechange = function () {
                 if (xhr.readyState === 4) {
                     if (xhr.status >= 200 && xhr.status < 300) {
                         //输出响应体
                        resolve(xhr.response);
                     } else {
                         //输出响应状态码
                         reject(xhr.status);
                     }
                 }
             }
         })
         p.then(value=>{
              console.log(value);
         },reason=>{
             //控制台输出警告信息
               console.warn(reason);
         })
     })

若把接口写错:

五:Promise封装ajax请求:

跟上一步差不多。就是把其封装在一个sendAJAX()的自定义函数里。

function sendAJAX(url) {
         return new Promise((resolve, reject) => {
                //创建对象
                const xhr = new XMLHttpRequest();
                xhr.responseType = 'json';
                //初始化
                xhr.open('GET', url);
                //发送
                xhr.send();
                //处理响应结果
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            //输出响应体
                            resolve(xhr.response);
                        } else {
                            //输出响应状态码
                            reject(xhr.status);
                        }
                    }
                }
            });
     }
      
     sendAJAX("http://poetry.apiopen.top/sentences")
     .then(value=>{
              console.log(value);
         },reason=>{
             //控制台输出警告信息
               console.warn(reason);
         })

六:promise的状态改变:

promise状态表示实例对象的一个属性
【PromiseState】。包括以下值:
(1)pending 未决定的
(2)resolved 或 fullfilled 成功
(3)rejected 失败
Promise对象的值表示实例对象的另一个属性
【PromiseResult】。保存着对象【成功/失败】的结果。而其状态改变只有以下两种可能:
(1)pending 变为resolved
(2)pending 变为 rejected
注:一个promise对象只能改变一次,无论成功或失败都会有一个结果数据,成功的称为 value , 失败的称为 reason 。

七:Promise基本流程图:

八:Promise的API 使用:

  1. Promise 的构造函数:Promise(executor){}
    (1)executor 函数:执行器 (resolve,reject)=> {}。
    (2)resolve 函数:内部定义成功时调用函数 value => {} 。
    (3)reject 函数:内部定义失败时调用函数 reason => {} 。
    注意:Promise内部会立同步即调用executor,异步操作在执行器里执行。

  2. Promise.prototype.then 方法: (onResolved, onRejected)=> {}
    (1) onResolved 函数:成功的回调函数 (value) => {}
    (2) onRejected 函数:失败的回调函数 (reason) => {}
    注:指定用于得到成功value的成功回调和用于得到失败reason的失败回调是返回一个新的promise对象。

  3. Promise.prototype.catch万法: (onRejected) => {}
    onRejected.函数: 失败的回调函数(reason)=> {}
    注:只是失败的调用。then()的语法糖,相当于: then(undefined, onRejected)。

  4. Promise.resolve 方法: (value)=> {}
    value: 成功的数据或promise对象
    注:如果传入的参数为非Promise类 型的对象,则返回的结果为成功promise对象,如果传入的参数为Promise 对象,则参数的结果决定了resolve 的结果。

  5. Promise.reject 方法: (reason) => {}
    reason: 失败的原因
    注:无论传入啥只返回一个失败的promise对象。

  6. Promise.all 方法: (promises)=> {}
    promises: 包含n个promise的数组
    注:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败。失败了返回那个失败值。

  7. Promise.race 方法: (promises)=> {}
    promises: 包含n个promise的数组
    注:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态。
    来一个例子:

 let p1 = new Promise((resolve,reject)=>{
          setTimeout(()=>{
              resolve('yes');
          },1000)
      })
      let p2 = Promise.resolve('success');
      let p3 = Promise.resolve('come');

      const result = Promise.race([p1,p2,p3]);
      console.log(result);

九:使用Promise面临的关键问题:

1.如何改变 promise的状态?

(1) resolve(value): 如果当前是pending就会变为resolved。
(2) reject(reason): 如果当前是pending就会变为rejected。
(3)抛出异常 throw :如果当前是pending就会变为rejected。

let p1 = new Promise((resolve,reject)=>{
           //  resolve('success');
         //    reject('error');
          //   throw 'error';
      })

2.一个 promise指定多个成功/失败回调函数,都会调用吗?

当promise改变为对应状态时都会调用。

let p = new Promise((resolve,reject)=>{
            resolve('success');
      })
      // 第一次回调
      p.then(value=>{
          console.log("yes");
      })
      // 第二次回调
      p.then(value=>{
          console.log("oh yes");
      })

3.改变 promiseT状态和指定回调函数谁先谁后?

(1)都有可能, 正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
(2)如何先改状态再指定回调?
①在执行 器中直接调用resolve(/reject();
②延迟更 长时间才调用then();
(3)什么时候才能得到数据?
①如果先指定的回调, 那当状态发生改变时,回调函数就会调用,得到数据
②如果先改变的状态, 那当指定回调时,回调函数就会调用,得到数据

4. promise.then()返回的新promise的结果状态由什么决定?

(1)简单表达: then()指定的回调函数执行的结果决定。
(2)详细表达:
*如果抛出异常, 新promise变为rejected, reaon为抛出的异常。
*如果返回的是非prormise的任意值,新promise变为resolved, value为返回的值。
*如果返回的是另一个新promise,此promise的结果就会成为新promise的结果。

let p = new Promise((resolve,reject) => {
            // resolve('success'); 
           // reject('No'); 
          //  throw 'oh no';
      });    
      let result = p.then(value => {
           console.log(value);
      }, reason => {
           console.warn(reason);   
      });     
      console.log(result);

5. promise 如何串连多个操作任务?

(1) promise 的then()返回一个新的promise,可以开成then()的链式调用。
(2)通过then的链式调用串连多个同步/异步任务。

 let p =new Promise((resolve,reject) => {
          resolve("yes");
     })
     p.then(value => {
          return new Promise((resolve,reject)=>{
              resolve("oh yes~");
          });
     }).then(value => {
          console.log(value);
     })

输出结果:
oh yes~

let p =new Promise((resolve,reject) => {
          resolve("yes");
     })
     p.then(value => {
          return new Promise((resolve,reject)=>{
              resolve("oh yes~");
          });
     }).then(value => {
          console.log(value);
     }).then(value => {
          console.log(value);
     })

输出结果:
oh yes~
undefined

6. promise 的异常穿透。

(1)当使用promise的then链式调用时,可以在最后指定失败的回调。
(2)前面任何操作出 了异常,都会传到最后失败的回调中处理。

 let p =new Promise((resolve,reject) => {
         setTimeout(()=>{
            resolve("yes");
         },1000);
     })
     p.then(value => {
          throw 'oh No';
     }).then(value => {
          console.log("123");
     }).then(value => {
          console.log("456");
     }).catch(reason=>{
         console.warn(reason);
     })

输出结果:
oh No

7.中断 promise链。

(1)当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数。
(2)办法:在回调函数中返回一个pendding状态的promise对象。
未中断:

 let p =new Promise((resolve,reject) => {
         setTimeout(()=>{
            resolve("yes");
         },1000);
     })
     p.then(value => {
          console.log("789");
     }).then(value => {
          console.log("123");
     }).then(value => {
          console.log("456");
     }).catch(reason=>{
         console.warn(reason);
     })

输出结果:
789
123
456

中断:

 let p =new Promise((resolve,reject) => {
         setTimeout(()=>{
            resolve("yes");
         },1000);
     })
     p.then(value => {
          console.log("789");
          return new Promise(()=>{});
     }).then(value => {
          console.log("123");
     }).then(value => {
          console.log("456");
     }).catch(reason=>{
         console.warn(reason);
     })

输出结果:
789

十:Promise的自定义封装:(略)

十一:async函数:

1.函数的返回值为promise对象。
2.promise对象的结果由async函数执行的返回值决定。
3.其实跟 then()方法返回结果是一样一样的。

1.返回一个非Promise对象,返回值是resolve。

async function main(){
             return '123';
          }
          let res = main();
          console.log(res);

**2.如果返回是一个Promise对象,由返回结果决定:** 如:
 async function main(){
             return new Promise((resolve,reject)=>{
                 reject('NO');
             });
          }
          let res = main();
          console.log(res);

3.抛出异常也是失败

 async function main(){
             return new Promise((resolve,reject)=>{
                 reject('NO');
             });
          }
          let res = main();
          console.log(res);

十二.await表达式:

1.await右侧的表达式一般为promise对象,但也可以是其它的值。
2.如果表达式是promise对象,await返回的是promise成功的值。
3.如果表达式是其它值,直接将此值作为await的返回值。

注意:

1.await 必须写在async函数中,但async 函数中可以没有await 。
2.如果await的promise失败了,就会抛出异常,需要通过try…catch捕获处理。

1.右侧为promise对象

 async function works(){
             let p = new Promise((resolve,reject)=>{
                 resolve('oh yes')
             })
             let res = await p;
             console.log(res);
         }
         works();

结果:
oh yes

2.右侧为其它值:

 async function works(){
             let p = new Promise((resolve,reject)=>{
                 resolve('oh yes')
             })
           //  let res = await p;
           let res = await 100;
             console.log(res);
         }
         works();

结果:
100

3.如果promise是失败状态:

 async function works(){
             let p = new Promise((resolve,reject)=>{
                // resolve('oh yes')
                reject('err');
             })          
            try{
                let res = await p;
            }catch(e){
                console.log(e);
            }
         }
         works();

十三.async与await结合发生ajax请求:

效果: 点击按钮获取一句名言。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">获取一句名言</button>
    <script>
        function sendAJAX(url) {
         return new Promise((resolve, reject) => {
                //创建对象
                const xhr = new XMLHttpRequest();
                xhr.responseType = 'json';
                //初始化
                xhr.open('GET', url);
                //发送
                xhr.send();
                //处理响应结果
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            //输出响应体
                            resolve(xhr.response);
                        } else {
                            //输出响应状态码
                            reject(xhr.status);
                        }
                    }
                }
            });
     }
         
        var btn = document.querySelector("#btn");
        btn.addEventListener('click',async function(){
             let word = await sendAJAX("http://poetry.apiopen.top/sentences");
              console.log(word);
        })
    </script>
</body>
</html>
posted @ 2021-11-19 19:42  青川薄  阅读(182)  评论(0)    收藏  举报