Promise超详细(含手撕)

目录

一、promise的操作

0.初体验-promise的使用

(1)抽奖案列,用一下promise

(2)promise进行文件操作:

(3)使用promise发送ajax请求

1.Promise封装fs读取文件操作 

2.Promise封装原生ajax GET请求

3.Promise实例对象的两个属性

(1)状态属性PromiseState

(2)结果值属性PromiseResult

4.Promise的工作流程

5.Promise的API

(1)then和catch

(2)Promise.resolve (value)

(3)Promise.reject(reason)

(4)Promise.all([p1,p2,p3])

二、一些关键问题

1.如何修改promise对象的状态

2.能否执行多个回调

3.改变promise状态和指定回调的区别

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

5.串联多个任务(链式调用)

6.异常穿透

7.中断Promise链

三、手撕Promise

1.搭建整体结构

2.resolve和reject的实现

3.throw抛出错误改变状态

4.状态一旦改变就不能再变

5.then方法的初步封装

5.异步任务回调的执行

6.执行多个then的回调

7.同步任务then方法的返回结果(难点)

8.异步任务then方法的返回结果(难点)

9.then方法中封装重复代码

11.Promise.resolve方法封装

12.Promise.reject方法封装

13.Promise.all方法封装(难点)

14.Promise.race方法封装

15.细节:then方法的回调是异步执行的

16.class版本

四、async函数和await表达式(难点)

1.async函数

2.await表达式

3.async和await结合


一、promise的操作

0.初体验-promise的使用
(1)抽奖案列,用一下promise

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>初体验</title>
</head>

<body>
  <div class="container">
    <h2>Promise 初体验</h2>
    <button class="btn btn-primary" id="btn">点击抽奖</button>
  </div>
</body>
<script>
  function rand(m, n) {
    return Math.ceil(Math.random() * (n - m + 1)) + m - 1
  }
  const btn = document.querySelector('#btn');
  btn.addEventListener('click', function () {

    // setTimeout(() => {
    //   let n = rand(1, 100);
    //   if (n <= 30) {
    //     alert('您未中奖')
    //   } else {
    //     alert('恭喜您中奖了')
    //   }
    // }, 1000)

    // promise里面传一个回调函数
    // 参数为resolve--解决--函数类型数据和reject--拒绝--函数类型数据 
    // 
    const p = new Promise((resolve, reject) => {
      // 异步操作放到回调函数里
      setTimeout(() => {
        let n = rand(1, 100);
        if (n <= 30) {
          // 成功调用这个函数
          resolve(n)         //将promise状态设置为成功
        } else {
          // 失败调用这个函数
          reject(n)        //将promise状态设置为失败
        }
        console.log(n);

      }, 1000)
    })
    // 状态成功执行第一个回调函数,失败执行第二个回调函数
    p.then((value) => {
      alert('恭喜您中奖了,中将号码为' + value)
    }, (reason) => {
      alert('您未中奖,号码为' + reason)
    })
  })
</script>
(2)promise进行文件操作:
const { log } = require('console')
const fs = require('fs')
// 回调形式
// fs.readFile('./resource/context.txt', (err, data) => {
//   if (err) throw error
//   console.log(data.toString())
// }),
const p = new Promise((resolve, reject) => {
  fs.readFile('./resource/context.txt', (err, data) => {
    // 如果失败调用 reject函数 并传入err参数
    if (err) reject(err)
    // 如果成功调用 resolve函数 并传入data参数
    resolve(data)
  })
})
p.then((value) => {
  console.log(value.toString())
}, (err) => {
  console.log(err)
})
(3)使用promise发送ajax请求

要熟练掌握发送原生ajax请求


<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div class="container"></div>
  <h2>Promise封装ajax</h2>
  <button class="btn btn-primary" id="btn">发送ajax请求</button>
  </div>
</body>
<script>
  const btn = document.querySelector('#btn')
  btn.addEventListener('click', function () {
    const p = new Promise((resolve, reject) => {
      // 1.创建对象
      const xhr = new XMLHttpRequest()
      // 2.初始化
      xhr.open('GET', 'https://www.baidu.com/')
      // 3.发送请求
      xhr.send()
      // 4.处理响应结果
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(xhr.response)
          } else {
            reject(xhr.status)
          }
        }
      }
    })
    p.then(response => {
      console.log(response)
    }, status => {
      console.log(status)
    })
  })

</script>
1.Promise封装fs读取文件操作 

 封装一个函数myReadFile 读取文件内容

 参数:path 文件路径

 返回:promise对象

function myReadFile(path) {
  return new Promise((resolve, reject) => {
    require('fs').readFile(path, (err, data) => {
      if (err) reject(err)
      resolve(data)
    })
  })
}
// 返回的是一个promise对象所以可以then
myReadFile('./resource/context.txt').then(value => {
  console.log(value.toString())
}, reason => {
  console.log(reason)
})

util.promisify()函数封装读取文件操作

// 引入util
const util = require('util')
// 引入fs
const fs = require('fs');
// 相当于把fs.readFile函数转为了promise风格
// 前面的参数你自己传,后面的回调函数代码固定,自动给你写好了
// 是err优先,并且参数都给你传好了 
// 所以能转成promise风格的函数也不多
// 函数返回promise
let myReadFile = util.promisify(fs.readFile);
myReadFile('./resource/context.txt').then(value => {
  console.log(value.toString())
}, reason => {
  console.log(reason)
})
2.Promise封装原生ajax GET请求
<script>
  function sendAjax(url) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest()
      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://api.apiopen.top/getJok').then(value => {
    console.log(value.toString())
  }, reason => {
    console.log(reason)
  })
</script>
3.Promise实例对象的两个属性
(1)状态属性PromiseState

        初始PromiseState的值为pending未决定的,之后成功就是pending变为resolved(或fullfilled),失败就变成了rejected。且一个promise对象只能改变一次,不可能从resolved变到rejected,也不可能从rejected变为resolved。

(2)结果值属性PromiseResult

保存着异步任务成功或失败的结果,只有resolve和reject接收到的值决定它的值。

4.Promise的工作流程

5.Promise的API
(1)then和catch

(2)Promise.resolve (value)

resolve属于函数对象而不是实例对象

value: 成功的数据或 promise 对象
说明 : 返回一个成功 / 失败的 promise 对象
    <script>
        //
        let p1 = Promise.resolve(521);
        //如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
        //如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
        let p2 = Promise.resolve(new Promise((resolve, reject) => {
            // resolve('OK');
            reject('Error');
        }));
        // console.log(p2);
        p2.catch(reason => {
            console.log(reason);
        })
    </script>
(3)Promise.reject(reason)

reason: 失败的原因

说明 : 返回一个失败的 promise 对象

无论value传入什么值,返回的都是失败的promise对象

(4)Promise.all([p1,p2,p3])

传入一个数组,若p1、p2、p3都是一个成功的promise对象则函数返回一个新的成功的promise对象,且属性值为p1、p2、p3属性值组成的数组。只要有一个失败返回的就是一个失败的promise对象,属性值为失败的那个promise的属性值。若有多个失败呢?只会有一个失败,后面的不执行。

成功:

  <script>
    let p1 = new Promise((resolve, reject) => {
      resolve('OK')
    })
    let p2 = Promise.resolve('yes')
    let p3 = Promise.resolve('year')
    console.log(Promise.all([p1, p2, p3]))
  </script>

失败:

  <script>
    let p1 = new Promise((resolve, reject) => {
      resolve('OK')
    })
    let p2 = Promise.reject('err')
    let p3 = Promise.reject('shibai')
    console.log(Promise.all([p1, p2, p3]))
  </script>

(5)Promise.race([p1,p2,p3])

返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态,race赛跑的意识,相当于数组里的promise进行赛跑,谁的状态先改变,race函数的返回结果状态和属性值就看它。
  <script>
    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('OK')
      }, 1000)

    })
    let p2 = Promise.reject('err')
    let p3 = Promise.reject('shibai')
    console.log(Promise.race([p1, p2, p3]))
  </script>

二、一些关键问题

1.如何修改promise对象的状态
(1) resolve(value): 如果当前是 pending 就会变为 resolved
(2) reject(reason): 如果当前是 pending 就会变为 rejected
(3) 抛出异常 : 如果当前是 pending 就会变为 rejected
2.能否执行多个回调
promise 改变为对应状态时都会调用
  <script>
    let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    p.then(value => {
      console.log('执行1')
    })
    p.then(value => {
      console.log('执行2')
    })
    // 结果俩个都执行
  </script>
3.改变promise状态和指定回调的区别

指定回调就是then,指定和执行不一样。

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

先指定回调再改变状态再执行回调函数


 <script>
  let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    })
    p.then(value => {
      console.log('执行1')
    }, reason => {

    })

  </script>
4..then(),.catch()返回的新 promise 的结果和状态由什么决定
简单表达 : then() 指定的回调函数执行的结果决定
详细表达 :
(1)如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
(2) 如果返回的是非 promise 的任意值 , promise 变为 resolved, value 为返回的值
(3)如果返回的是另一个新 promise, promise 的结果就会成为新 promise 的结果
  let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    let result = p.then(value => {
      // throw 'Error'
      // return 520
      return new Promise((resolve, reject) => {
        resolve('success')
      })
    }, reason => {

    })
    console.log(result)

5.串联多个任务(链式调用)
(1)promise then() 返回一个新的 promise, 可以开成 then() 的链式调用
(2)通过 then 的链式调用串连多个同步 / 异步任务
  let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    let result = p.then(value => {
      // throw 'Error'
      // return 520
      return new Promise((resolve, reject) => {
        resolve('success')
      })
    }).then(value => {
      console.log(value)  //上一个是新promise对象调用then,value为success 
    }).then(value => {
      console.log(value) //输出为undefined,因为上一个then返回新的promise,没有返回值,所以没属性值,根据规则是状态是成功
    })
    console.log(result)
6.异常穿透
        当使用 promise then 链式调用时 , 可以在最后指定失败的回调, 前面任何操作出了异常, 都会传到最后失败的回调中处理。
   let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    let result = p.then(value => {
      // throw 'Error'
      // return 520
      return new Promise((resolve, reject) => {
        resolve('success')
      })
    }).then(value => {
      console.log(111)
      throw 'erroe'
    }).then(value => {
      console.log(222)
    }).then(value => {
      console.log(333)
    }).catch(reason => {
      console.warn(reason)
    })

上面出现的错误会在最后的catch里被处理

7.中断Promise链
当使用 promise then 链式调用时 , 在中间中断 , 不再调用后面的回调函数,
只有一种办法在回调函数中返回一个 pendding 状态的 promise 对象,只有是pendding状态才不会执行下面的任何代码。
  let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    let result = p.then(value => {
      // throw 'Error'
      // return 520
      return new Promise((resolve, reject) => {
        resolve('success')
      })
    }).then(value => {
      console.log(111)
    }).then(value => {
      console.log(222)
      // return new Promise((resolve, reject) => { })
      return new Promise(() => { })
    }).then(value => {
      console.log(333)
    }).catch(reason => {
      console.warn(reason)
    })

三、手撕Promise

1.搭建整体结构

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./promise.js"></script>
</head>

<body>
  <script>
    let p = new Promise((resolve, reject) => {
      resolve('OK')
    });
    p.then(value => {
      console.log(value)
    }, reason => {
      console.log(reason)
    })
  </script>
</body>

</html>

在index.html中引入promise.js,并在其中设置两个函数,去覆盖原来的Promise和then,并且使用构造函数时要同步调用executor

function Promise(executor) {
  // resolve函数
  function resolve(data) {

  }
  // reject函数
  function reject(data) {

  }
  // 同步调用函数执行器
  executor(resolve, reject)
}
Promise.prototype.then = function (onResolved, onRejected) {

}
2.resolve和reject的实现

注意里面resolve函数this指向window的两种解决办法

function Promise(executor) {
  this.promiseState = 'pending'
  this.promiseResult = null
  const self = this
  // resolve函数
  //如果写成function,里面的this指向为window,所以写成箭头函数
  // 或者在外面用self保存好this
  resolve = (data) => {
    // 1.功能:修改对象的状态promiseState
    this.promiseState = 'fulfilled'
    // 2.设置对象结果值(promiseResult)
    this.promiseResult = data
  }
  // reject函数
  function reject(data) {
    // 1.功能:修改对象的状态promiseState
    self.promiseState = 'rejected'
    // 2.设置对象结果值(promiseResult)
    self.promiseResult = data
  }
  // 同步调用函数执行器
  executor(resolve, reject)
}
Promise.prototype.then = function (onResolved, onRejected) {

}
3.throw抛出错误改变状态

throw抛出错误,用catch接到仔调用reject改变promise状态和属性值

  try {
    // 同步调用函数执行器
    executor(resolve, reject)
  } catch (e) {
    // 修改promise状态为失败
    reject(e)
  }
4.状态一旦改变就不能再变

只需在里面加个promiseState的判断

  // resolve函数
  //如果写成function,里面的this指向为window,所以写成箭头函数
  // 或者在外面用self保存好this
  resolve = (data) => {
    // 判断状态,只有pending的时候才能改变状态
    // 即状态只能改变一次
    if (this.promiseState !== 'pending') return
    // 1.功能:修改对象的状态promiseState
    this.promiseState = 'fulfilled'
    // 2.设置对象结果值(promiseResult)
    this.promiseResult = data
  }
  // reject函数
  function reject(data) {
    if (self.promiseState !== 'pending') return
    // 1.功能:修改对象的状态promiseState
    self.promiseState = 'rejected'
    // 2.设置对象结果值(promiseResult)
    self.promiseResult = data
  }
5.then方法的初步封装

在then方法里加上两个判断并调用函数

  if (this.promiseState === 'fulfilled') {
    onResolved(this.promiseResult)
  }
  if (this.promiseState === 'rejected') {
    onRejected(this.promiseResult)
  }
5.异步任务回调的执行

        异步执行,状态还没有改变,就去执行then了,而因为then没有对状态为pending的处理,所以执行不成功。我们需要在then加上对pending状态的处理。把函数保存到自己的callback上,然后在状态改变之后调用。

function Promise(executor) {
  this.promiseState = 'pending'
  this.promiseResult = null
  this.callback = {}
  const self = this
  // resolve函数
  //如果写成function,里面的this指向为window,所以写成箭头函数
  // 或者在外面用self保存好this
  resolve = (data) => {
    // 判断状态,只有pending的时候才能改变状态
    // 即状态只能改变一次
    if (this.promiseState !== 'pending') return
    // 1.功能:修改对象的状态promiseState
    this.promiseState = 'fulfilled'
    // 2.设置对象结果值(promiseResult)
    this.promiseResult = data
    // 状态改变了再去执行then的回调函数
    if (this.callback.onResolved) {
      this.callback.onResolved(data)
    }

  }
  // reject函数
  function reject(data) {
    if (self.promiseState !== 'pending') return
    // 1.功能:修改对象的状态promiseState
    self.promiseState = 'rejected'
    // 2.设置对象结果值(promiseResult)
    self.promiseResult = data
    // 状态改变了再去执行then的回调函数
    if (self.callback.onRejected) {
      self.callback.onRejected(data)
    }
  }


  try {
    // 同步调用函数执行器
    executor(resolve, reject)
  } catch (e) {
    // 修改promise状态为失败
    reject(e)
  }

}
Promise.prototype.then = function (onResolved, onRejected) {
  // 分别判断,调用这两个函数
  if (this.promiseState === 'fulfilled') {
    onResolved(this.promiseResult)
  }
  if (this.promiseState === 'rejected') {
    onRejected(this.promiseResult)
  }
  if (this.promiseState === 'pending') {
    // 要把回调函数保存到自身上。
    this.callback = {
      onResolved: onResolved,
      onRejected: onRejected
    }
  }
}
6.执行多个then的回调

        当在异步情况下执行多个then函数时,上面callback的保存方法会出现覆盖的情况,所以我们要把它改为数组,并且在状态改变后要用遍历的方式去调用

function Promise(executor) {
  this.promiseState = 'pending'
  this.promiseResult = null
  this.callbacks = []
  const self = this
  // resolve函数
  //如果写成function,里面的this指向为window,所以写成箭头函数
  // 或者在外面用self保存好this
  resolve = (data) => {
    // 判断状态,只有pending的时候才能改变状态
    // 即状态只能改变一次
    if (this.promiseState !== 'pending') return
    // 1.功能:修改对象的状态promiseState
    this.promiseState = 'fulfilled'
    // 2.设置对象结果值(promiseResult)
    this.promiseResult = data
    // 状态改变了再去执行then的回调函数
    this.callbacks.forEach((item) => {
      item.onResolved(data)
    })
  }
  // reject函数
  function reject(data) {
    if (self.promiseState !== 'pending') return
    // 1.功能:修改对象的状态promiseState
    self.promiseState = 'rejected'
    // 2.设置对象结果值(promiseResult)
    self.promiseResult = data
    // 状态改变了再去执行then的回调函数
    self.callbacks.forEach((item) => {
      item.onRejected(data)
    })
  }


  try {
    // 同步调用函数执行器
    executor(resolve, reject)
  } catch (e) {
    // 修改promise状态为失败
    reject(e)
  }

}
Promise.prototype.then = function (onResolved, onRejected) {
  // 分别判断,调用这两个函数
  if (this.promiseState === 'fulfilled') {
    onResolved(this.promiseResult)
  }
  if (this.promiseState === 'rejected') {
    onRejected(this.promiseResult)
  }
  if (this.promiseState === 'pending') {
    // 要把回调函数保存到自身上。
    this.callbacks.push({
      onResolved: onResolved,
      onRejected: onRejected
    })
  }
}
7.同步任务then方法的返回结果(难点)

实现then的返回值,它的值是一个Promise,其状态和结果值与回调函数返回的结果有关。如果回调函数返回的是promise,则then返回的promise的结果和状态和他一样。如果回调函数返回的不是promise,则then返回的promise的状态为成功,值是回调函数返回的值(没有就是undefined)。如果抛出异常,状态是异常,值为抛出的值。

 <script>
    let p = new Promise((resolve, reject) => {
      resolve('chenggong')
      // setTimeout(() => {
      //   reject('shibai')
      // }, 1000)

      // throw 'shibai'
    });
    // console.log(p)
    const res = p.then(value => {
      // console.log(value)
      // 实现then的返回值,如果回调函数返回的是Promise则状态和结果与Promise有关
      // return 'hello Promise'
      return new Promise((resolve, reject) => {
        reject('shibai')
      })
      // 抛出异常
      // throw 'Fail'
    }, reason => {
      console.log(reason)
    })
    // p.then(value => {
    //   alert(value)
    // }, reason => {
    //   alert(reason)
    // })
    // console.log(p)
    console.log(res)
  </script>


Promise.prototype.then = function (onResolved, onRejected) {
  // then的返回值是一个promise
  return new Promise((resolve, reject) => {
    // 分别判断,调用这两个函数
    if (this.promiseState === 'fulfilled') {
      try {
        // 获取回调函数的执行结果
        let result = onResolved(this.promiseResult)
        if (result instanceof Promise) {
          // 如果是Promise对象,修改当前要返回的Promise的状态和值
          result.then(value => {
            resolve(value)
          }, reason => {
            reject(reason)
          })
        } else {
          // 结果对象状态为成功
          resolve(result)
        }
      } catch (e) {
        reject(e)
      }
    }
    if (this.promiseState === 'rejected') {
      onRejected(this.promiseResult)
    }
    if (this.promiseState === 'pending') {
      // 要把回调函数保存到自身上。
      this.callbacks.push({
        onResolved: onResolved,
        onRejected: onRejected
      })
    }
  })

}
8.异步任务then方法的返回结果(难点)

和同步差不多,关键是改callbacks里的函数

  <script>
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('chenggong ')
      }, 1000)
    });
    const res = p.then(value => {
      throw 'Fail'
    }, reason => {
      console.log(reason)
    })
    console.log(res)
  </script>



   if (this.promiseState === 'pending') {
      // 要把回调函数保存到自身上。
      this.callbacks.push({
        onResolved: function () {
          try {
            // 执行成功的回调函数
            let result = onResolved(self.promiseResult)
            // 判断
            if (result instanceof Promise) {
              result.then(value => {
                resolve(value)
              }, reason => {
                reject(reason)
              })
            } else {
              resolve(result)
            }
          } catch (e) {
            reject(e)
          }

        },
        onRejected: function () {
          let result = onRejected(self.promiseResult)
          try {
            // 判断
            if (result instanceof Promise) {
              result.then(value => {
                resolve(value)
              }, reason => {
                reject(reason)
              })
            } else {
              resolve(result)
            }
          } catch (e) {
            reject(e)
          }
        }
      })
    }
  })
9.then方法中封装重复代码

把判断是不是Promise的代码来修改then返回的promise的状态和值的那段代码进行封装

Promise.prototype.then = function (onResolved, onRejected) {
  const self = this
  // then的返回值是一个promise
  return new Promise((resolve, reject) => {
    function callback(type) {
      try {
        // 获取回调函数的执行结果
        let result = type(self.promiseResult)
        if (result instanceof Promise) {
          // 如果是Promise对象,修改当前要返回的Promise的状态和值
          result.then(value => {
            resolve(value)
          }, reason => {
            reject(reason)
          })
        } else {
          // 结果对象状态为成功
          resolve(result)
        }
      } catch (e) {
        reject(e)
      }
    }
    // 分别判断,调用这两个函数
    if (this.promiseState === 'fulfilled') {
      callback(onResolved)
    }
    if (this.promiseState === 'rejected') {
      callback(onRejected)
    }
    if (this.promiseState === 'pending') {
      // 要把回调函数保存到自身上。
      this.callbacks.push({
        onResolved: function () {
          callback(onResolved)
        },
        onRejected: function () {
          callback(onRejected)
        }
      })
    }
  })

}

10.catch方法封装,异常穿透(难点)

下面那段代码写进then方法里,如果then没有传成功或失败的回调函数,就默认给你创建一个

// 添加catch方法
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected)
}

//写在then方法里

 if (typeof onRejected !== 'function') {
    onRejected = reason => {
      throw reason
    }
  }
  if (typeof onResolved !== 'function') {
    onResolved = value => value
  }
11.Promise.resolve方法封装
// 添加resolve方法
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(v => {
        resolve(v)
      }, r => {
        reject(r)
      })
    } else {
      resolve(value)
    }
  })
}
12.Promise.reject方法封装
// 添加reject方法
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}
13.Promise.all方法封装(难点)

// 返回一个promise,数组中的promise状态全为成功就是成功,
// 值是所有成功的promise组成的数组

// 添加all方法
// 返回一个promise,数组中的promise状态全为成功就是成功,
// 值是所有成功的promise组成的数组
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let count = 0
    let arr = []
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        count++
        arr[i] = v
        if (count === promises.length) {
          resolve(arr)
        }
      }, r => {
        this.reject(r)
      })
    }
  })
}
14.Promise.race方法封装
// 添加race方法
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        resolve(v)
      }, r => {
        reject(r)
      })
    }
  })
}
15.细节:then方法的回调是异步执行的

它是异步执行的,其它同步代码执行完它才会执行,只需要在resolve、reject函数还有下图两个位置加个定时器,即调用onResolved和onRejected函数的地方

执行结果应该是132

    if (this.promiseState === 'fulfilled') {
      setTimeout(() => {
        callback(onResolved)
      })

    }
    if (this.promiseState === 'rejected') {
      setTimeout(() => {
        callback(onRejected)
      });
16.class版本
class Promise {
  constructor(executor) {
    this.promiseState = 'pending'
    this.promiseResult = null
    this.callbacks = []
    const self = this
    // resolve函数
    //如果写成function,里面的this指向为window,所以写成箭头函数
    // 或者在外面用self保存好this
    function resolve(data) {
      // 判断状态,只有pending的时候才能改变状态
      // 即状态只能改变一次
      if (self.promiseState !== 'pending') return
      // 1.功能:修改对象的状态promiseState
      self.promiseState = 'fulfilled'
      // 2.设置对象结果值(promiseResult)
      self.promiseResult = data
      // 状态改变了再去执行then的回调函数
      setTimeout(() => {
        self.callbacks.forEach((item) => {
          item.onResolved(data)
        })
      })

    }
    // reject函数
    function reject(data) {
      if (self.promiseState !== 'pending') return
      // 1.功能:修改对象的状态promiseState
      self.promiseState = 'rejected'
      // 2.设置对象结果值(promiseResult)
      self.promiseResult = data
      // 状态改变了再去执行then的回调函数
      setTimeout(() => {
        self.callbacks.forEach((item) => {
          item.onRejected(data)
        })
      })

    }


    try {
      // 同步调用函数执行器
      executor(resolve, reject)
    } catch (e) {
      // 修改promise状态为失败
      reject(e)
    }

  }
  then(onResolved, onRejected) {
    const self = this
    if (typeof onRejected !== 'function') {
      onRejected = reason => {
        throw reason
      }
    }
    if (typeof onResolved !== 'function') {
      onResolved = value => value
    }
    // then的返回值是一个promise
    return new Promise((resolve, reject) => {
      function callback(type) {
        try {
          // 获取回调函数的执行结果
          let result = type(self.promiseResult)
          if (result instanceof Promise) {
            // 如果是Promise对象,修改当前要返回的Promise的状态和值
            result.then(value => {
              resolve(value)
            }, reason => {
              reject(reason)
            })
          } else {
            // 结果对象状态为成功
            resolve(result)
          }
        } catch (e) {
          reject(e)
        }
      }
      // 分别判断,调用这两个函数
      if (this.promiseState === 'fulfilled') {
        setTimeout(() => {
          callback(onResolved)
        })

      }
      if (this.promiseState === 'rejected') {
        setTimeout(() => {
          callback(onRejected)
        });

      }
      if (this.promiseState === 'pending') {
        // 要把回调函数保存到自身上。
        this.callbacks.push({
          onResolved: function () {
            callback(onResolved)
          },
          onRejected: function () {
            callback(onRejected)
          }
        })
      }
    })
  }
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }
  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(v => {
          resolve(v)
        }, r => {
          reject(r)
        })
      } else {
        resolve(value)
      }
    })
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      let arr = []
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(v => {
          count++
          arr[i] = v
          if (count === promises.length) {
            resolve(arr)
          }
        }, r => {
          this.reject(r)
        })
      }
    })
  }
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(v => {
          resolve(v)
        }, r => {
          reject(r)
        })
      }
    })
  }
}

四、async函数和await表达式(难点)

1.async函数

(1)函数的返回值是promise对象

(2)promise对象的结果由asynac函数执行的返回值决定

2.await表达式

(1)await右侧的表达式一般为promise对象,但也可以是其它值

(2)如果表达式是promise对象,await返回的是promise成功的值

(3)如果表达式是其它值,直接将此值作为await的返回值

注意

1.await必须写在async函数中,但async函数可以没有await

2.如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理

 <script>
    async function main() {
      // 1.右侧为promise的情况
      let p = new Promise((resolve, reject) => {
        reject('shibai')
      })
      try {
        let res = await p
      } catch (e) {
        console.log(e)
      }

      let res2 = await 20
      console.log(res2)
    }
    main()
  </script>

3.async和await结合

可以避免回调地狱的问题

(1)文件读取

const fs = require('fs')
const util = require('util')
const myRead = util.promisify(fs.readFile)
async function main() {
  try {
    let data1 = await myRead('./resource/context.txt')
    console.log(data1.toString())
  } catch (e) {
    console.log(e)
  }
}
main()

<think>我们被要求详细解释JavaScript中的Promise,包括使用方法、原理及最佳实践。根据用户提供的引用内容,我们可以整合以下信息:引用[1]说明了Promise的then()方法行为: -如果未提供onRejected处理函数或未使用.catch(),错误会一直传递到Promise链的末端- then()方法的返回值类型决定了链式调用的行为:-返回普通值:新Promise直接成功-返回Promise:新Promise跟随其状态-返回thenable对象:展开后生成新Promise引用[2]展示了Promise的then方法实现的关键部分:- then方法需要处理边界情况(非函数参数)- then方法返回一个新的Promisepromise2) -需要在新的Promise中处理回调函数引用[3]给出了Promise的基本概念:- Promise是异步编程的解决方案-从语法上说,promise是一个对象,代表异步操作的最终状态-Promise是一个构造函数,有reject、resolve、all等方法,原型上有then、catch等方法现在,我们将结合这些引用和已知知识,详细展开讲解。#JavaScript Promise详解##一、Promise基本概念PromiseJavaScript中处理**异步操作**的核心机制,它代表一个异步操作的最终完成(或失败)及其结果值[^3]。Promise对象有三种状态:- **Pending(等待中)**:初始状态- **Fulfilled(已成功)**:操作成功完成-**Rejected(已失败)**:操作失败状态转换是不可逆的:一旦从 Pending变为 Fulfilled或Rejected,状态就固定了。##二、Promise使用方法###1.创建Promise使用 `new Promise()`构造函数创建Promise实例: ```javascriptconst promise= newPromise((resolve,reject)=> {//异步操作(如API请求、定时器等)setTimeout(()=> {constsuccess =true;//模拟操作结果if(success) {resolve('Operationsucceeded');//状态变为Fulfilled}else {reject('Operationfailed');//状态变为Rejected}},1000); });``` ###2.处理Promise结果使用`.then()`处理成功状态,`.catch()`处理失败状态:```javascriptpromise.then(result=> {console.log(result); //"Operationsucceeded"}).catch(error=> {console.error(error); //"Operationfailed"});``` ###3. Promise链式调用`.then()`方法返回一个新的Promise,支持链式调用: ```javascriptfetch('/api/data').then(response =>response.json()) //返回新Promise.then(data=> processData(data))//继续处理.catch(error =>handleError(error));//捕获整个链中任何错误``` ###4.静态方法|方法|描述 ||------|------| |`Promise.resolve(value)` |创建已解决的Promise || `Promise.reject(reason)`|创建已拒绝的Promise || `Promise.all(iterable)` |所有Promise成功时返回结果数组,任一失败立即拒绝 || `Promise.race(iterable)` |返回第一个解决或拒绝的 Promise结果| |`Promise.allSettled(iterable)`|所有 Promise完成后返回结果数组(ES2020) |##三、Promise核心原理###1.then方法实现机制`.then()`方法是 Promise的核心,其实现要点包括[^2]:1.**参数规范化**:将非函数参数转换为默认函数```javascriptonFulfilled =typeof onFulfilled ==='function' ?onFulfilled: value=> value;onRejected =typeof onRejected=== 'function'? onRejected: err=> {throw err};``` 2. **返回新Promise**:确保链式调用中每个 `.then()`都返回独立的Promise```javascriptthen(onFulfilled, onRejected) {constpromise2= newPromise((resolve,reject)=> {//实现回调执行和状态传递逻辑});returnpromise2;}``` 3. **状态传递规则**:-若回调返回普通值,`promise2`用该值解决-若回调返回Promise,`promise2`跟随其状态-若回调返回thenable对象,展开后解决`promise2`###2.错误传播机制Promise链具有**冒泡特性**:-未被处理的错误会沿链向下传递-直到被`.catch()`或第二个`.then()`参数捕获```javascriptPromise.reject("Error").then(()=> console.log("Success"))//跳过.catch(err=> console.error(err));//捕获错误```###3.微任务队列Promise回调作为**微任务(Microtask)**执行:-比常规任务(如 setTimeout)优先级更高-确保异步操作顺序一致性```javascriptconsole.log('Start'); setTimeout(()=> console.log('Timeout'),0); Promise.resolve().then(()=> console.log('Promise')); console.log('End');//输出顺序: Start→End →Promise →Timeout```##四、最佳实践###1.错误处理规范-**始终添加`.catch()`**:避免未处理的Promise拒绝```javascript//错误示范fetchData(); //未处理可能的拒绝//正确做法fetchData().catch(error=> logError(error));``` -**使用`async/await+try/catch`**:使异步代码具有同步错误处理结构```javascriptasync functionloadData() {try{const data= awaitfetchData();return process(data);} catch(error) {handleError(error);}}``` ###2.避免嵌套陷阱- **扁平化链式调用**:取代回调地狱(Callback Hell)```javascript//不推荐fetchUser(userId,user=> {fetchPosts(user.id, posts=> {processPosts(posts);});});//推荐fetchUser(userId).then(user=> fetchPosts(user.id)).then(posts =>processPosts(posts));``` ###3.性能优化-**合理使用 `Promise.all`**:并行独立异步操作```javascriptconst [user,posts] =await Promise.all([fetchUser(userId),fetchPosts(userId)]);```- **避免不必要的链**:减少微任务数量提升性能```javascript//冗余链promise.then(data=> data)//无操作.then(processData);//优化后promise.then(processData);``` ##五、常见问题解决###1.同步错误捕获构造函数中的同步错误会被自动捕获: ```javascriptnew Promise(()=> {thrownew Error('Sync error');//自动转换为拒绝}).catch(err=> console.error(err));``` ###2.中断 Promise链返回一个永远处于 pending状态的Promise实现中断:```javascriptfetchData().then(data =>{if (!data.valid){returnnew Promise(() =>{});//中断链}return process(data);}).then(()=> console.log("Done")); //不会执行``` ###3.全局拒绝处理监听未处理的 Promise拒绝: ```javascript//浏览器环境window.addEventListener('unhandledrejection',event =>{console.error('Unhandledrejection:',event.reason);}); //Node.jsprocess.on('unhandledRejection',(reason,promise)=> {console.error('Unhandled Rejection at:', promise, 'reason:',reason);}); ```---§§相关问题§§1. Promise与Async/Await在错误处理机制上有哪些本质区别? 2.如何实现Promise的取消功能?有哪些现有方案?3.在 Promise链中,微任务队列是如何影响代码执行顺序的?4.Promise.all和Promise.allSettled分别适用于哪些实际场景?5.为什么说Promise不能完全取代回调函数?在哪些场景下回调仍是必要的?[^1]:JavaScript Promise的then()方法详解[^2]:JS—Promise:原理解析[^3]:JavaScriptPromise详解</think>## JavaScript Promise 详解:原理、使用与最佳实践 ### 一、Promise 核心概念 PromiseJavaScript 处理异步操作的标准化方案,代表一个**异步操作的最终状态**(成功/失败)及其结果值[^3]。其核心特点: 1. **状态不可逆**: - `pending`(进行中)→ `fulfilled`(已成功) - `pending` → `rejected`(已失败) 2. **链式调用**:通过 `.then()` 和 `.catch()` 实现操作序列化 3. **错误冒泡**:未处理的拒绝状态会沿链传递直到被捕获[^1] ### 二、Promise 工作原理 #### 核心实现机制(伪代码) ```javascript class MyPromise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state !== 'pending') return; this.state = 'fulfilled'; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn(value)); }; const reject = (reason) => { if (this.state !== 'pending') return; this.state = 'rejected'; this.value = reason; this.onRejectedCallbacks.forEach(fn => fn(reason)); }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { // 边界处理:确保回调是函数 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; // 返回新Promise实现链式调用[^2] return new MyPromise((resolve, reject) => { // ... 处理回调执行和状态传递 }); } } ``` #### 关键流程解析: 1. **初始化**:executor 立即执行,接收 resolve/reject 函数 2. **状态变更**:resolve/reject 调用后更新状态并触发回调 3. **回调队列**:在 pending 状态时存储回调函数 4. **链式传播**:then() 返回新 Promise 传递处理结果 ### 三、Promise 使用方法 #### 1. 基础使用 ```javascript // 创建Promise const fetchData = new Promise((resolve, reject) => { setTimeout(() => { Math.random() > 0.5 ? resolve('Data received') : reject('Network error') }, 1000); }); // 结果处理 fetchData .then(data => console.log(data)) .catch(err => console.error(err)); ``` #### 2. 链式调用规则 `.then()` 返回值决定下一链状态[^1]: ```javascript // 返回普通值 → 新Promise立即解决 Promise.resolve(1) .then(x => x + 2) // 返回3 .then(console.log); // 输出3 // 返回Promise → 跟随其状态 Promise.resolve() .then(() => Promise.reject('error')) .catch(console.error); // 必须捕获 // 返回thenable对象 → 展开处理 Promise.resolve() .then(() => ({ then(resolve) { resolve('Thenable resolved'); } })) .then(console.log); // 输出"Thenable resolved" ``` #### 3. 静态方法 | 方法 | 描述 | 示例 | |------|------|------| | `Promise.all()` | 所有成功时返回结果数组 | `Promise.all([p1, p2])` | | `Promise.race()` | 采用第一个完成的结果 | `Promise.race([req, timeout])` | | `Promise.allSettled()` | 所有完成后返回状态数组 | `Promise.allSettled([p1, p2])` | | `Promise.any()` | 采用第一个成功的结果 | `Promise.any([p1, p2])` | ### 四、最佳实践与陷阱规避 #### ✅ 推荐实践 1. **始终返回链式**:避免嵌套 Promise ```javascript // 👍 扁平化结构 fetchUser() .then(user => fetchProfile(user.id)) .then(profile => render(profile)); // 👎 回调地狱 fetchUser().then(user => { fetchProfile(user.id).then(profile => { render(profile) }) }) ``` 2. **全局错误处理**: ```javascript // 浏览器环境 window.addEventListener('unhandledrejection', event => { console.error('Unhandled rejection:', event.reason); }); // Node.js process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection at:', promise, 'reason:', reason); }); ``` #### ⚠️ 常见陷阱 1. **忘记返回值**: ```javascript // ❌ 下一链接收undefined Promise.resolve(1) .then(x => { x + 1 }) // 无return .then(console.log); // 输出undefined ``` 2. **同步错误未捕获**: ```javascript new Promise(() => { throw new Error('Sync error'); // 会被自动拒绝 }).catch(console.error); // 必须捕获 ``` 3. **忽略异步错误**: ```javascript // ❌ 错误未被处理 Promise.reject('error') .then(console.log); // 缺少catch // ✅ 正确方式 Promise.reject('error') .catch(console.error); ``` ### 五、Promise 与 Async/Await ```javascript // Async/Await 本质是Promise语法糖 async function loadData() { try { const user = await fetchUser(); const posts = await fetchPosts(user.id); return { user, posts }; } catch (error) { handleError(error); } } // 等效Promise写法 function loadData() { return fetchUser() .then(user => fetchPosts(user.id)) .then(posts => ({ user, posts })) .catch(handleError); } ``` ### 六、进阶原理 #### 微任务(Microtask)机制 Promise 回调通过**微任务队列**执行,优先级高于宏任务(setTimeout等): ```javascript console.log('Start'); setTimeout(() => console.log('Timeout'), 0); Promise.resolve() .then(() => console.log('Promise 1')) .then(() => console.log('Promise 2')); console.log('End'); /* 输出顺序: Start End Promise 1 Promise 2 Timeout */ ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值