前端必刷系列之红宝书——第 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 版)

相关推荐
古怪今人1 分钟前
[前端]HTML盒模型与尺寸,标准文档流,块级元素、内联元素和行内块,CSS选择器
前端·css
小雨下雨的雨22 分钟前
基于鸿蒙PC Electron框架技术完成的表单验证技术详解
前端·javascript·华为·electron·前端框架·鸿蒙
提子拌饭13323 分钟前
饮料含糖量查询应用 - 鸿蒙PC用Electron框架完整实现
前端·javascript·华为·electron·前端框架·鸿蒙
JustHappy25 分钟前
古法编程秘籍(五):什么是进程和线程?从软件到 CPU 的一次完整旅程
前端·后端·代码规范
爱编程的小金32 分钟前
前端请求库的下一个进化方向:从 Promise 到策略化
前端·alova·前端请求库·请求策略
hsg7733 分钟前
简述:Jensen Huang‘s Footsteps网站全内容分析
前端·javascript·数据库
珑墨1 小时前
前端 AI 开发通用skill
前端
kyriewen1 小时前
一个人+Cursor,7天上线付费小程序:第1天我就想放弃了
前端·微信小程序·cursor
大家的林语冰1 小时前
Angular 王者归来,第 22 个主版本亮相,一大波前沿技术再度引领潮流!
前端·javascript·前端框架
嵌入式ZYXC1 小时前
第2篇:《面试题:LDO和DC-DC的区别?分别用在什么场景?》
stm32·单片机·嵌入式硬件·面试·职场和发展