前端必刷系列之红宝书——第 11 章

"红宝书" 通常指的是《JavaScript 高级程序设计》,这是一本由 Nicholas C. Zakas(尼古拉斯·扎卡斯)编写的 JavaScript 书籍,是一本广受欢迎的经典之作。这本书是一部翔实的工具书,满满的都是 JavaScript 知识和实用技术。

不管你有没有刷过红宝书,如果现在还没掌握好,那就一起来刷红宝书吧,go!go!go!

系列文章:

第一部分:基本知识(重点、反复阅读)

  1. 前端必刷系列之红宝书------第 1、2 章
  2. 前端必刷系列之红宝书------第 3 章
  3. 前端必刷系列之红宝书------第 4、5 章
  4. 前端必刷系列之红宝书------第 6 章

第二部分:进阶内容(重点、反复阅读)

  1. 前端必刷系列之红宝书------第 7 章
  2. 前端必刷系列之红宝书------第 8 章
  3. 前端必刷系列之红宝书------第 9 章
  4. 前端必刷系列之红宝书------第 10 章
  5. 前端必刷系列之红宝书------第 11 章

第 11 章 期约与异步函数

ES6 新增期约。ES8 新增异步函数。ES 的异步编程特性有了长足的进步。

通过期约 Promise 和异步函数 async/await,不仅可以实现之前难以实现或不可能实现的任务,而且也能写出更清晰、简洁,并且容易理解、调试的代码。

期约

在 JavaScript 中,期约(Promise) 是一种用于处理异步操作的对象。期约提供了一种更结构化的方式来处理异步代码,避免了回调地狱(Callback Hell)的问题。

期约的主要功能是为异步代码提供了清晰的抽象。可以用期约表示异步执行的代码块,也可以用期约表示异步计算的值

在需要串行异步代码时,期约的价值最为突出。

作为可塑性极强的一种结构,期约可以被序列化、连锁使用、复合、扩展和重组

期约基础

  • 期约是一种用于处理异步操作的对象,具有三个状态:pending(等待中)、fulfilled(已成功)和rejected(已拒绝)。
  • 通过new Promise(executor)构造函数创建期约,executor函数包含异步操作的代码,并接受resolvereject两个参数。
  • 使用.then()处理成功状态,.catch()处理拒绝状态,.finally()处理期约结束时的逻辑。

期约链式调用

  • .then()返回一个新的期约,可以通过链式调用.then().catch()形成期约链。
  • 在链式调用中,每个.then()都可以返回一个值或期约,传递给下一个.then()

.all() 和 .race() 和 .allSettled()

  • Promise.all(iterable)用于处理多个期约,只有所有期约都成功才算成功,任何一个期约失败都将导致整体失败。
  • Promise.race(iterable)在第一个期约完成时返回其结果,无论成功还是拒绝。
  • Promise.allSettled(iterable):静态方法,返回一个期约,等到所有期约都已解决或拒绝后才解决,结果是一个数组,每个元素都包含一个对象,表示对应的期约是解决还是拒绝。

.resolve() 和 .reject()

  • Promise.resolve(value)创建一个已解决的期约,将value作为期约的结果。
  • Promise.reject(reason)创建一个已拒绝的期约,将reason作为期约的拒因。

错误处理

  • 使用.catch()try/catch语句捕获期约的拒绝。
  • 可以通过在.then()链中使用第二个参数处理错误。

手写 Promise

js 复制代码
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

function myPromise(fn) {
  const self = this

  this.state = PENDING
  this.value = null
  this.resolvedCallbacks = []
  this.rejectedCallbacks = []

  function resolve(value) {
    if(value instanceof myPromise) {
      return value.then(resolve, reject)
    }

    setTimeout(() => {
      if (self.state === PENDING) {
        self.state = RESOLVED
        self.value = value
        self.resolvedCallbacks.forEach((callback) => {
          callback(value)
        })
      }
    }, 0)
  }

  function reject(value){
    setTimeout(()=> {
      if (self.state === PENDING) {
        self.state = REJECTED
        self.value = value
        self.rejectedCallbacks.forEach((callback) => {
          callback(value)
        })
      }
    }, 0)
  }

  try{
    fn(resolve, reject)
  }catch(err){
    reject(err)
  }
}

myPromise.prototype.then = (onResolve, onReject) => {
  onResolve = typeof onResolve === 'function' ? onResolve : function(value) { return value }

  onReject = typeof onReject === 'function' ? onReject : function(error) { throw new Error('错误') }

  if (this.state === PENDING) {
    this.resolvedCallbacks.push(onResolve)
    this.rejectedCallbacks.push(onReject)
  }

  if (this.state === RESOLVED) {
    onResolve(this.value)
  }
  if(this.state === REJECTED) {
    onReject(this.value)
  }
}

手写Promise.then

js 复制代码
function then(onResolve, onReject) {
  const self = this

  return new Promise((resolve, reject) => {
    let fulfilled = () => {
      try{
        const res = onResolve(self.value)
        return res instanceof myPromise ? res.then(resolve, reject) : resolve(res)
      }catch(err){
        reject(err)
      }
    }

    let rejected = () => {
      try{
        const res = onReject(self.reason)
        return res instanceof myPromise ? res.then(resolve, reject) : reject(res)
      }catch(err){
        reject(err)
      }
    }

    switch(self.status){
      case PENDING:
        self.resolvedCallbacks.push(fulfilled)
        self.rejectedCallbacks.push(rejected)
        break;
      case RESOLVED:
        fulfilled()
        break;
      case REJECT:
        rejected()
        break;
    }
  })
}

手写Promise.all

js 复制代码
function promiseAll(promises){
  if(!Array.isArray(promises)){
    throw new Error('must be an array')
  }
  return new Promise((resolve, reject) => {
    const count = promises.length
    let num = 0
    const res = []

    for(let i=0; i<count; i++){
      Promise.resolve(promises[i]).then((value)=>{
        num++
        res[i] = value
        if(res.length === count){
          resolve(res)
        }
      }).catch(error => {
        reject(error);
      });
    }
  })
}

手写Promise.race

js 复制代码
function promiseRace(promises){
  if(!Array.isArray(promises)){
    throw new Error('must be an array')
  }
  return new Promise((resolve, reject)=>{
    for(let i=0; i<promises.length; i++){
      promises[i].then(value => {
          resolve(value);
        })
        .catch(error => {
          reject(error);
        });
    }
  })
}

手写Promise.any

js 复制代码
function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      reject(new TypeError('promises must be an array'));
    }

    const errors = [];

    promises.forEach(promise => {
      Promise.resolve(promise)
        .then(result => {
          resolve(result);
        })
        .catch(error => {
          errors.push(error);
          if (errors.length === promises.length) {
            reject(new AggregateError('All promises were rejected', errors));
          }
        });
    });
  });
}

手写Promise.allSettled

js 复制代码
function promiseAllSettled(promises) {
  return new Promise(resolve => {
    if (!Array.isArray(promises)) {
      reject(new TypeError('promises must be an array'));
    }

    const results = [];
    let completedPromises = 0;

    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(result => {
          results[index] = { status: 'fulfilled', value: result };
        })
        .catch(reason => {
          results[index] = { status: 'rejected', reason: reason };
        })
        .finally(() => {
          completedPromises++;
          if (completedPromises === promises.length) {
            resolve(results);
          }
        });
    });
  });
}

手写Promise.finally

js 复制代码
Promise.prototype.finally = (cb) => {
  return this.then(
    value => Promise.resolve(cb()).then(()=>value),
    err => Promise.reject(cb()).then(()=> throw new Error(err))
  )
}

异步函数

异步函数通常是指使用async/await语法糖来处理异步操作的函数。

异步函数可暂停执行,而不阻塞主线程

async 函数

  • async 关键字用于定义一个异步函数。异步函数返回一个期约(Promise)。
  • 异步函数内部可以包含await表达式,它会暂停函数执行,等待期约解决,并返回期约的结果。

await 表达式

  • await 表达式只能在异步函数内部使用,用于等待期约的解决。
  • await后面可以是一个期约对象,也可以是返回期约的函数调用或任何返回值为期约的表达式。

错误处理

  • 使用 try/catch 语句来捕获异步函数内的错误。
  • 如果期约被拒绝,将会抛出一个异常,可以通过catch捕获。
js 复制代码
async function example() {
  try {
    const result = await someAsyncFunction();
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

多个异步操作的串行执行

  • 使用 await 可以实现多个异步操作的串行执行,确保一个操作完成后再执行下一个。
js 复制代码
async function serialAsyncOperations() {
  const result1 = await operation1();
  const result2 = await operation2(result1);
  const result3 = await operation3(result2);
  return result3;
}

未完待续...

参考资料

《JavaScript 高级程序设计》(第 4 版)

相关推荐
new出一个对象2 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥3 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森4 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy4 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189114 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿5 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡6 小时前
commitlint校验git提交信息
前端
虾球xz7 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇7 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒7 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript