JavaScript Promise、async、await(笔记)

1. 认识Promise

  • Promise 是一门新技术(ES6规范)
  • Promise 是 JS 中进行异步编程新的解决方案,旧的使用的是回调函数
  • 从语法上来说,Promise 是一个构造函数
  • 从功能上来说,Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果、
  • Promise.then 之后会返回一个新的 Promise 对象,支持链式调用,可以解决回调地狱的问题

2. Promise初体验

简单案例

// 实现一个彩票中奖的案例,中奖概率为30%,公共代码

// 生成一个 min~max 之间的随机数
function random(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min)
}
// 获取点击按钮
let btn = document.querySelector("button")
// 不基于Promise实现
btn.addEventListener("click", function () {
  setTimeout(() => {
    if (random(1, 100) <= 30) {
      alert("恭喜你中奖了")
    } else {
      alert("很遗憾没中奖")
    }
  }, 2000)
})
// 基于Promise实现
btn.addEventListener("click", function () {
  const p = new Promise((resolve, reject) => {
    setTimeout(() => {
      let n = random(1, 100)
      if (n <= 30) {
        resolve(n)
      } else {
        reject(n)
      }
    }, 2000)
  })
  p.then(res => {
    alert("恭喜你中奖了" + res)
  }).catch(err => {
    alert("很遗憾没中奖" + err)
  })
})

3. Promise的状态与值

3.1 状态
  • 实例对象中的一个属性 [PromiseState],一个 Promise 对象只能改变一次成功或失败的状态

  • pending:待定

  • resolved/fullfilled:成功

  • rejectd:失败,与成功只能有一个

  • settled/resolved:以敲定/已决议,表示 Promise 对象无论成功还是失败,都会进入这个状态

3.2 值
  • 实例对象中的一个属性 [PromiseResult]
  • 保存着对象成功/失败的结果
  • 只有 resolve 和 reject 才能改变这个值

4. Promise方法的使用

4.1 then、finally
  • then

    // 只使用 then 返回异常
    function sum(a, b) {
      return new Promise((resolve, reject) => {
        if (a && b) {
          resolve(a + b)
        } else {
          reject("出错了")
        }
      })
    }
    
    // then 可以传两个参数(成功,失败)
    sum(5).then(res => { console.log(res); }, err => { console.log(err); }) // 出错了
    

    注意:

    Promise 可以连续 .then,最后跟一个 .catch 就可以了(异常穿透)

    .then 返回的一个新的 Promise 对象状态为成功,新 Promise 对象的值为 return 的值

  • finally

    // finally 无论失败与成功都会执行它
    function sum(a, b) {
      return new Promise((resolve, reject) => {
        if (a && b) {
          resolve(a + b)
        } else {
          reject("出错了")
        }
      })
    }
    
    // 成功
    sum(5, 10).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    }).finally(() => {
      console.log("我会被执行");
    })
    // 15
    // 我会被执行
    
    // 失败
    sum(5).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    }).finally(() => {
      console.log("我会被执行");
    })
    // 出错了
    // 我会被执行
    
4.2 Promise静态方法
  • Promise.all( iterable ):这个方法返回一个新的 promise 对象,返回多个 promise 成功的值,并存放数组中,如果有一个失败 all 就会返回那个失败
  • Promise.any( iterable ):接收一个 Promise 对象的集合,当其中的一个 promise 成功,就返回那个成功的 promise 的值,如果全部失败,会抛出全部承诺被拒绝
  • Promise.allSettled( iterable ):返回一个 promise 对象,无论成功失败,都会把对应的状态和值返回
  • Promise.race( iterable ):只会返回其中一个处理好的 promise,无论成功还是失败
  • Promise.reject( err ):相当于直接调用 .catch,如果参数是一个 promise 对象,会返回这个 Promise 对象
  • Promise.resolve( res ):相当于直接调用 .then,如果参数是一个 promise 对象,那么这个 promise 对象返回的值,就是 resolve 的值

5. Promise的实现

class Promise {
  constructor(executor) {
    // 待定 pending
    // 成功 fullfilled
    // 失败 rejectd
    this.PromiseState = "pending"
    this.PromiseResult = undefined
    // 保存 then 中的回调,可能会被调用多次
    this.callbacks = []

    let self = this

    // 抛出异常也为失败回调
    // Promise 中只为同步抛出异常做了处理,异步没有
    try {
      // Promise 中的回调函数
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }

    // 成功
    function resolve(res) {
      // 只能改变一次状态的值
      if (self.PromiseState === "pending") {
        // 1.改变状态
        self.PromiseState = "fullfilled"
        // 2.改变返回值
        self.PromiseResult = res
        // 如果 resolve 为同步,callbacks 为空不会被调用 then 中的方法
        if (self.callbacks.length !== 0) {
          setTimeout(() => {
            self.callbacks.forEach(item => {
              item.onResolve(res)
            })
          });
        }
      }
    }

    // 失败
    function reject(err) {
      if (self.PromiseState === "pending") {
        self.PromiseState = "rejectd"
        self.PromiseResult = err
        if (self.callbacks.length !== 0) {
          setTimeout(() => {
            self.callbacks.forEach(item => {
              item.onReject(err)
            })
          });
        }
      }
    }
  }

  // .then
  then(onResolve, onReject) {
    // 处理异常穿透
    if (typeof onReject !== "function") {
      // 如果 then 中没有处理失败,那么就一值向传
      onReject = err => {
        throw err
      }
    }

    // 值传递
    if (typeof onResolve !== "function") {
      // 如果 then 中没有写成功的回调,那么就给它创建一个
      onResolve = res => res
    }

    return new Promise((resolve, reject) => {
      // 模拟异步任务
      setTimeout(() => {
        // 两个回调只能执行一个
        if (this.PromiseState === "fullfilled") {
          this.thenAll(onResolve, resolve, reject)
        }

        if (this.PromiseState === "rejectd") {
          this.thenAll(onReject, resolve, reject)
        }

        // 如果 resolve() 或 reject() 为异步
        // 就把函数存入数组中,让 resolve 来调用
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            // 延迟成功的处理
            onResolve: () => {
              this.thenAll(onResolve, resolve, reject)
            },
            // 延迟失败的处理
            onReject: () => {
              this.thenAll(onReject, resolve, reject)
            }
          })
        }
      })
    })
  }

  // then 中公共的逻辑
  thenAll(on, resolve, reject) {
    try {
      let result = on(this.PromiseResult)
      if (result instanceof Promise) {
        // 借助 then 中返回的的值,来确定返回 Promise 的值与状态
        // 如果没有返回或返回其他值,默认为成功的状态
        result.then(res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      } else {
        resolve(result)
      }
    } catch (e) {
      reject(e)
    }
  }

  // .catch
  catch(onReject) {
    return this.then(undefined, onReject)
  }

  // Promise.resolve
  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      } else {
        resolve(value)
      }
    })
  }

  // Promise.reject
  static reject(value) {
    return new Promise((resolve, reject) => {
      reject(value)
    })
  }

  // Promise.all
  static all(promises) {
    if (!Array.isArray(promises)) throw "必须为可迭代对象"
    return new Promise((resolve, reject) => {
      let arr = []
      let count = 0
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(res => {
          // 不确定哪个先执行,要保证顺序不被打乱
          arr[i] = res
          count++
          if (count === promises.length) {
            resolve(arr)
          }
        }, err => {
          // 有一个失败就为失败
          reject(err)
        })
      }
    })
  }

  // Promise.race
  static race(promises) {
    if (!Array.isArray(promises)) throw "必须为可迭代对象"
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        // 因为状态只能修改一次,所以不需要加任何判断
        promises[i].then(res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      }
    })
  }
}

6. async、await

6.1 async

async 函数会返回一个 Promise 对象,和 Promise.resolve 有点相似

// 正常同步执行
function fn(a, b) {
  return a + b
}

console.log(111);
console.log(fn(10000, 10000));
console.log(222);
// 111 
// 20000
// 222
// 加了 async,并没有改变代码执行顺序
// async 函数会返回一个 Promise 对象,Promise 是同步的
async function fn(a, b) {
  return a + b
}

console.log(111);
console.log(fn(10000, 10000));
console.log(222);
// 111 
// Promise {<fulfilled>: 20000}
// 222
// Promise 有.then 和 .catch 方法,是异步的
// 因为 async 函数会返回一个 Promise 对象,所以可以使用 .then
async function fn(a, b) {
  return a + b
}

console.log(111);
fn(10000, 10000).then(res => {
  console.log(res);
})
console.log(222);
// 111 
// 222
// 20000 ==> 异步的丢掉最后执行的
6.2 await
  • await 操作符用于等待一个 Promise 对象。它只能在异步函数 async function 或 模板顶层 type=“module” 使用
  • await 表达式会暂停当前 async function 的执行,并不会阻塞外部同步代码的执行,等待 async function 函外部的同步代码执行完成以后,才会在执行 await 之后的代码
  • await 返回 Promise 对象的成功结果,如果为失败会把异常原因抛出(如果没有使用 await 抛出,外部无法使用 try{}catch{}去捕获,只能使用 .catch 内部捕获)。如果等待的不是 Promise 对象,则返回该值本身
  • await 等待之后的代码会放入微任务队列,但是紧跟在 await 后面的代码为同步的(下面案例的 333)
async function fn(){
  console.log(111);
  let count = await 333
  console.log(count);
}
fn()

console.log(222);
// 111
// 222
// 333 await 微任务
new Promise((resolve, reject) => {
  resolve(444)
}).then(res => {
  console.log(res);
})
async function fn() {
  console.log(111);
  let count = await 333
  console.log(count);
}
fn()
console.log(222);
// 111
// 222
// 444 .then 微任务,先进入的微任务队列
// 333 await 微任务,后进入的微任务队列
setTimeout(() => {
  console.log(444);
})
async function fn() {
  console.log(111);
  let count = await 333
  console.log(count);
}
fn()
console.log(222);
// 111 同步
// 222 同步
// 333 await 微任务
// 444 setTimeout 宏任务最后执行
async function fn() {
  // 接收 Promise 成功的值
  let result = await new Promise((resolve, reject) => {
    resolve(111)
  })

  console.log(result);
}
fn() // 111
async function fn() {
  // 抛出 Promise 失败的值
  let result = await new Promise((resolve, reject) => {
    reject(111)
  })

  console.log(result);
}
// 并没有使用 catch 去捕获
fn() // Uncaught (in promise) 111

7. 特殊用法 thenable

当一个函数或对象有 then 方法时,可以作为 Promise 使用

let obj = {
  then(resolve, reject) {
    resolve("ok1");
  },
};

function fn() {}

fn.then = function (resolve, reject) {
  resolve("ok2");
};

// 在 Promise 中会默认调用 then 方法,此刻的 then 和 new Promise 很相似
Promise.resolve(obj)
  .then((res) => {
    console.log(res); // "ok1"
    return fn;
  })
  .then((res) => {
    console.log(res); // "ok2"
  });

8. 关于 thenable 会慢一步,Promise.resolve 会慢两步的问题

8.1 thenable 慢一步
// 使用 thenlable 会多生成一个回调会导致慢一步
let obj = {
  then(resolve, reject) {
    resolve();
  },
};

function fn() {}

fn.then = function (resolve, reject) {
  resolve();
};

Promise.resolve(obj)
  .then((res) => {
    console.log("ok1");
    return fn;
  })
  .then((res) => {
    console.log("ok2");
  });

Promise.resolve().then(()=>{
  console.log("ok3")
})

// ok3 ok1 ok2
8.2 Promise.resolve 慢两步
// 如果在 then 中返回一个 Promise.resolve,会把 then 中整个 then 函数丢到微任务队列
// 这样在微任务队列中会再次调用 then 生成回调,导致慢两步
Promise.resolve()
  .then(() => {
    console.log(0);
    return Promise.resolve();
  })
  .then(() => {
    console.log(4);
  });

Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(5);
  })
  .then(() => {
    console.log(6);
  });
  
// 0 1 2 3 4 5 6
8.3 两种情况结合
let obj = {
  then(resolve, reject) {
    resolve();
  },
};

function fn() {}

fn.then = function (resolve, reject) {
  resolve();
};

Promise.resolve(obj)
  .then(() => {
    console.log("ok1");
  })
  .then(() => {
    console.log("ok2");
  })
  .then(() => {
    console.log("ok5");
  });

Promise.resolve()
  .then(() => {
    console.log("ok3");
    return Promise.resolve();
  })
  .then((res) => {
    console.log("ok4");
  });

// ok3 ok1 ok2 ok5 ok4
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值