Promise使用详解

简介: JS查漏补缺系列是我在学习JS高级语法时做的笔记,通过实践费曼学习法进一步加深自己对其的理解,也希望别人能通过我的笔记能学习到相关的知识点。这一次我们来了解Promise使用详解

异步请求的处理方式

案例要求:模拟网络请求,通过请求结果来调用成功的回调函数/调用失败的回调函数

在没有使用promise之前,需要callback来调用(自己封装好并定义好名称,要使用的时候才能调用)

function requestData(url, successCallback, failureCallback) {
  // 模拟网络请求
  setTimeout(() => {
    // 拿到请求的结果
    // url传入的是aaa, 请求成功
    if (url === "aaa") {
      // 成功
      let names = ["abc", "cba", "nba"]
      successCallback(names)
    } else { // 否则请求失败
      // 失败
      let error = "请求失败, url错误"
      failureCallback(error)
    }
  }, 3000);
}

// 成功的回调函数
function successCallback(result) {
  console.log("请求成功" + result);
}

// 失败的回调函数
function failureCallback(error) {
  console.log(error);
}

requestData("aac", successCallback, failureCallback)

弊端:因为是自定义的回调函数的名称和方法,所以在开发使用的时候要查看源码知道名称和方法后才能调用函数,增加了工作量
更好的方案:统一名称 promise,并规范好所有的代码编写逻辑

Promise

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。

简单使用:

function foo() {
// 传入的这个函数, 被称之为 executor
// > resolve: 回调函数, 在成功时, 回调resolve函数
// >reject: 回调函数, 在失败时, 回调reject函数
  return new Promise((resolve, reject) => {
    resolve("success message")
    // reject("failture message")
  })
}

const fooPromise = foo()
// 调用resolve(请求成功)会来到then方法
fooPromise.then((res) => {
  console.log(res)
}
// 调用reject(请求失败)会来到catch方法
fooPromise.catch((err) => {
  console.log(err)
})

用promise重构原来的代码去解决案例(异步请求处理):

function requestData(url,) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url === "aaa") {
        // 成功
        let names = ["abc", "cba", "nba"]
        resolve(names)
      } else { 
        // 失败
        let errMessage = "请求失败, url错误"
        reject(errMessage)
      }
    }, 3000);
  })
}

// main.js
const promise = requestData("aaa")
promise.then((res) => {
  console.log("请求成功:", res)
})
promise.catch((err) => {
   console.log("请求失败:", err)
})

在node中then和catch分开会报错
promise中的then和catch可以合并,给then传入两个回调函数:

第一个回调函数, 会在Promise执行resolve函数时, 被回调
第二个回调函数, 会在Promise执行reject函数时, 被回调
const promise = requestData("aaa")
promise.then((res) => {
  console.log("请求成功:", res)
}, (err) => {
   console.log("请求失败:", err)
})

promise的三种状态

一个 Promise 必然处于以下几种状态之一:

  • _待定(pending)_:初始状态,既没有被兑现,也没有被拒绝。(resolve函数和reject函数都没有被调用)
  • _已兑现(fulfilled)_:意味着操作成功完成。(已调用resolve函数)
  • _已拒绝(rejected)_:意味着操作失败。(已调用reject函数)

当resolve函数已经被调用时,promise的状态就被确定了,这时再去调用reject函数是没有效果的,反之亦然。

Promise 的链式调用

连续执行两个或者多个异步操作(一个promise包含着多个promise)
在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现连续执行两个或者多个异步操作的需求。

一个promise:

new Promise((resolve, reject) => {
  resolve('res message')
}).then(res => {
  console.log("res:", res)
}, err => {
  console.log("err:", err)
})

两个promise(当前promise的状态由传入promise决定):

const promise = new Promise((resolve, reject) => {
  // resolve("aaaaaa")
  reject("err message")
})

const newPromise = new Promise((resolve, reject) => {
  resolve(Promise)
}).then(res => {
  console.log("res:", res)
}, err => {
  console.log("err:", err)
})

Promise对象方法

// 可以通过下面代码查看Promise有哪些对象方法
console.log(Object.getOwnPropertyDescriptors(Promise.prototype))

Promise.prototype.then()

简单使用:

const promise = new Promise((resolve, reject) => {
  resolve()
})
promise.then(res => {
  console.log('res:',res)
})  // 当上面调用resolve时会调用下面的回调

同一个Promise可以被多次调用then方法,当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用

promise.then(res => {
  console.log("res1:", res)
})

promise.then(res => {
  console.log("res2:", res)
})

promise.then(res => {
  console.log("res3:", res)
})

then方法传入的 "回调函数: 可以有返回值,返回值是新的Promise

  • 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个的Promise的resolve
// 下面是一个链式调用
promise.then(res => {
  return "aaaaaa"  // 没有返回值默认返回undefined
}).then(res => {
  console.log("res:", res)
  return "bbbbbb"
})

上面代码中第二个then方法接收的值是第一个then方法返回的,且上面调用resolve是与第二个then方法无关的(注意:这里两个then方法所捕获的Promise不是同一个,第二个then方法捕获的是第一个then方法返回产生的一个的新Promise)

  • 如果我们返回的是一个Promise,则后一个的then方法所捕获的Promise取决于前一个返回的Promise
promise.then(res => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(111111)
    }, 3000)
  })
}).then(res => {
  console.log("res:", res)
})

Promise.prototype.catch()

通过catch方法来传入错误(拒绝)捕获的回调函数

简单使用:

const promise = new Promise((resolve, reject) => {
  reject()
})
promise.catch(err => {
  console.log('err:',err)
})

注意:catch捕获顺序

  1. 当上面调用的是resolve时:
  • 如果我们的catch方法是写在then方法之后的,当then方法返回的是一个普通值,那then方法和catch方法捕获的是同一个Promise。
  • 但如果then方法返回的是一个新的Promise且调用了reject/throw Error,则这个then方法之后的catch方法优先捕获的是新的Promise
const promise = new Promise((resolve, reject) => {
  resolve('111')
})
promise.then(res => {
  return new Promise((resolve, reject) => {
    reject("then rejected status")
  })
}).catch(err => {
  console.log("err:", err) // err: then rejected status
})
  1. 当上面调用的是reject时:
  • catch方法捕获的是与then方法同一个Promise,无论then方法返回的是什么值
const promise = new Promise((resolve, reject) => {
  reject('111')
})
promise.then(res => {
  return new Promise((resolve, reject) => {
    reject("then rejected status")
  })
}).catch(err => {
  console.log("err:", err) // err: 111
})

catch返回值:返回值也是新的Promise

  • 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个的Promise的resolve值(与then方法一样)
  • 如果我们希望后续继续执行catch,那么需要抛出一个异常

Catch 的后续链式操作

在回调的时候抛出错误之后想要再次进行新的操作则可以使用catch来实现
new Promise((resolve, reject) => {
    console.log('开始回调-------');
    resolve();
})
.then(() => {
    throw new Error('error message');

    console.log('aaa');
})
.catch(() => {
    console.log('bbb');
})
.then(() => {
    console.log('ccc');
});

在还没有开始执行之前,VSCode就已经检测出第一个then里面抛出错误之后的代码不会执行:
image.png
执行输出:
image.png
总结:当出现失败的情况时,使用catch()中断(在catch回调和抛出错误的then回调之间的then回调也是不会执行的,catch回调之后的then回调可以执行)

Promise.prototype.finally()

finally() 方法返回一个 Promise。在 promise 结束时, 无论结果是 fulfilled 或者是 rejected, 会执行指定的回调函数。
这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then()catch() 中各写一次的情况
const promise = new Promise((resolve, reject) => {
  // resolve("resolve message")
  reject("reject message")
})

promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
}).finally(() => {
  console.log("finally code execute")
})

Promise类方法

直接通过类名调用的方法

Promise.resolve

将普通对象转成Promise对象并调用resolve
  1. 传入普通的值
const promise = Promise.resolve({ name: "why" })
// 相当于
const promise2 = new Promise((resolve, reject) => {
  resolve({ name: "why" })
})
  1. 传入Promise
const promise = Promise.resolve(new Promise((resolve, reject) => {
  resolve("11111")
}))

Promise.reject

将普通对象转成Promise对象并调用reject
const promise = Promise.reject("rejected message")
// 相当于
const promise2 = new Promsie((resolve, reject) => {
  reject("rejected message")
})

Promise.all

所有的Promise都变成fulfilled状态时(所有Promise都调用resolve之后), 再拿到结果
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

Promise.all([p2, p1, p3, "aaaa"]).then(res => {
  console.log(res) // [22222,11111,33333,'aaaa']
})

但是在拿到所有结果之前, 有一个promise变成了rejected状态, 那么整个promise是rejected状态(Promise.all方法会被中断)

Promise.allSettled

所有的Promise都有结果(无论是fulfilled状态还是rejected状态)之后, 再拿到结果
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

// allSettled
Promise.allSettled([p1, p2, p3]).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

image.png

Promise.race

只要有一个Promise变成fulfilled状态, 那么就结束,但有一个Promise状态先变为rejected,则结束

Promise.any

等到至少有一个Promise变成fulfilled状态, 才就结束,不管有没有Promise状态先变为rejected。如果全为rejected状态则等到全部执行完才结束并执行catch方法(全部rejected状态的错误信息)
目录
相关文章
|
6月前
|
前端开发 API 容器
说说你对 promise 的了解
说说你对 promise 的了解
33 0
|
前端开发 JavaScript 测试技术
6 # 实现简单的 promise
6 # 实现简单的 promise
38 0
|
3月前
|
前端开发 JavaScript
Promise相关的理解
总之,Promise 是现代 JavaScript 异步编程的基石。它们提供了一种优雅的方式来处理异步操作,易于读写和维护,还可以通过其方法来控制复杂的异步流程。
53 5
|
5月前
|
前端开发
|
6月前
|
前端开发 JavaScript
Promise 详解
Promise 详解
63 0
|
11月前
|
前端开发
Promise
Promise
57 1
|
存储 JSON 前端开发
深入使用 Promise
前面各种铺垫已经好了,现在我们一起来用一用Promise!
73 0
|
前端开发
对promise的理解分享
对promise的理解分享
|
前端开发 JavaScript
深入理解Promise
深入理解Promise
88 0
|
前端开发 JavaScript
探索Promise的应用
探索Promise的应用
119 0