js【详解】Promise(含 Promise 的三种状态及其变化,创建 Promise, Promise.resolve,then 方法,Promise.all, Promise 的串行和并行)

为什么需要使用 Promise ?

传统回调函数的代码层层嵌套,形成回调地狱,难以阅读和维护,为了解决回调地狱的问题,更加优雅地书写复杂的异步任务,诞生了 Promise

什么是 Promise ?

Promise 是一种异步编程的解决方案,本身是一个构造函数,可通过 new 创建未来会执行的对象实例(一个状态为 pending 的 Promise 实例 )

console.log(Promise); // [Function: Promise]

自带 resolve,reject,all 等方法,其原型上还有 then、catch 等方法。

Promise 的三种状态及其变化

  1. pending 进行中,不会触发 then 和 catch 回调函数
  2. resolved / fulfilled 已成功,会触发后续的 then 回调函数
  3. rejected 已失败,会触发后续的 catch 回调函数
    在这里插入图片描述

Promise 的状态变化如上图所示,不可逆

  • Promise 最初的状态是 pending

  • pending 状态的 Promise 执行 resolve() 后,状态变为 resolved

    Promise.resolve(); // Promise 的状态从 pending 变为 resolved
    
  • resolved 状态的 Promise 会触发后续的 then 函数,

    • 若 then 函数内没有报错,则返回一个 resolved 状态的 Promise

      Promise.resolve().then(() => {}); // 最终 Promise 的状态为 resolved
      
    • 若 then 函数内报错,则返回一个 rejected 状态的 Promise

      Promise.resolve().then(() => {
        throw new Error("then函数出现报错");
      }); // 最终 Promise 的状态为 rejected
      
  • pending 状态的 Promise 执行 reject() 后,状态变为 rejected

    Promise.reject(); // Promise 的状态从 pending 变为 rejected
    
  • rejected 状态的 Promise 会触发后续的 catch 函数,

    • 若 catch 函数内没有报错,则返回一个 resolved 状态的 Promise

      Promise.reject().catch(() => {}); // 最终 Promise 的状态为 resolved
      
    • 若 catch 函数内报错,则返回一个 rejected 状态的 Promise

      Promise.reject().catch(() => {
        throw new Error("catch函数出现报错");
      }); // 最终 Promise 的状态为 rejected
      

创建 Promise

新创建的 Promise 实例的状态为 pending

// 此时,p1 的状态为 pending
const p1 = new Promise((resolve, reject)=>{
 
})

执行 resolve() ,Promise 实例的状态变为 fulfilled

const p1 = new Promise((resolve, reject) => {
  resolve()
})

执行 reject() ,Promise 实例的状态变为 rejected

const p1 = new Promise((resolve, reject) => {
  reject()
})

Promise.resolve()

  • 不传参数时,直接返回一个resolved状态的 Promise 对象。
Promise.resolve().then(function () {
  console.log('two'); // two
});
  • 参数为 Promise 实例时,原封不动地返回这个实例。
  • 参数为 thenable对象(有then方法的对象)时
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.resolve 会将这个对象转为 Promise 对象,然后就立即执行 then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
  • 参数若没有 then 方法,返回一个新的状态为 resolved 的 Promise 对象。
const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s) // Hello
});

Promise.resolve方法的参数,会同时传给回调函数

Promise 的 then 方法

  • 第一个参数是 Promise 成功解析时要调用的函数,该函数的参数为 Promise 的结果(可选)
  • 第二个参数是 Promise 被拒绝时要调用的函数(可选)
  • 返回一个新的 Promise 实例,其结果为第一个参数的返回值,若第一个参数的返回值为一个 Promise 实例,则 then 方法的返回值会是该 Promise 实例的深拷贝
// p1为结果为 100 的 fulfilled 状态的 Promise 实例
let p1 = new Promise((resolve, reject) => {
  resolve(100);
});

// p2为结果为 200 的 fulfilled 状态的 Promise 实例
let p2 = p1.then((res) => {
  console.log(res); // 打印 p1 的结果 100

  return 200;
});

若 then() 不传参数,则返回一个 pending 状态的 Promise

如果有多个 fulfilled promise 实例,同时执行 then 链式调用,then 会交替执行,这是编译器的优化,防止一个 promsie 占据太久时间

then 中返回promise 实例,相当于多出一个 promise 实例,也会遵守“交替执行”
(但和直接声明一个 promise 实例,结果有些差异)

then 中返回的 promise 实例,需要经历两次微任务,才能执行其 then 函数

  1. 将 promise 的状态从 pending 变为 fulfilled
  2. 将 then 函数放入微任务

待第3次微任务,才会执行返回的 promise 实例的 then 函数

// 面试题
Promise.resolve()
  .then(() => {
    console.log(0);
    return Promise.resolve(4);
  })
  .then((res) => {
    console.log(res);
  });

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

Promise 的串行执行

用 async await 实现最便捷

async function promise_serial() {
  let result = [];

  result.push((await promise_list[2]).data);
  result.push((await promise_list[1]).data);
  result.push((await promise_list[0]).data);

  console.log(result);
}

promise_serial();

另一种思路是:将 promise 包装为函数返回值,通过 then 链式调用执行

import axios from "axios";

let id_list = ["1", "2", "3"];

let promise_list = [];

for (let id of id_list) {
  promise_list.push(
    axios.get(`http://jsonplaceholder.typicode.com/users/${id}`)
  );
}

let p_func_dic = {};

promise_list.forEach((p_item, index) => {
  p_func_dic[index] = function () {
    return p_item;
  };
});

let result = [];

p_func_dic[2]()
  .then((res) => {
    result.push(res.data);

    return p_func_dic[1]();
  })
  .then((res) => {
    result.push(res.data);

    return p_func_dic[0]();
  })
  .then((res) => {
    result.push(res.data);

    console.log(result);
  });

当然,在 then 中执行下一个 Promise 的层层嵌套方式也是 Promise 串行,但因行成 回调地狱,不方便维护,不推荐使用。

Promise 的并行执行 —— Promise.all()

Promise .all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);
  • p1、p2、p3 都是 Promise 实例,得到的 p 也是Promise 实例
  • Promise .all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
  • p1、p2、p3 的状态都变成 fulfilled, p 的状态才会变成 fulfilled
  • p1、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected
  • p 的 then 函数中得到的是 p1、p2、p3 的返回值组成的一个数组

promise.all 实战范例

import axios from 'axios'

let infoList = []

let id_list = ['1', '2', '3']

let promise_list = []

for (let id of id_list) {
  promise_list.push(axios.get(`http://jsonplaceholder.typicode.com/users/${id}`))
}

Promise.all(promise_list).then((res) => {
  infoList = res.map((item) => item.data)
  console.log(infoList) // 得到预期结果
})

手写 promise.all

function pAll (_promises) {
    return new Promise((resolve, reject) => {
      // Iterable => Array
      const promises = Array.from(_promises)
      // 结果用一个数组维护
      const r = []
      const len = promises.length
      let count = 0
      for (let i = 0; i < len; i++) {
        // Promise.resolve 确保把所有数据都转化为 Promise
        Promise.resolve(promises[i]).then(o => { 
          // 因为 promise 是异步的,保持数组一一对应
          r[i] = o;
  
          // 如果数组中所有 promise 都完成,则返回结果数组
          if (++count === len) {
            resolve(r)
          }
          // 当发生异常时,直接 reject
        }).catch(e => reject(e))
      }
    })
 }

Promise 自测题

在这里插入图片描述
此时仅创建了 Promise 对象,没有执行 resolve() 或 reject(),所以状态是 pending

在这里插入图片描述

  • 因 setTimeout 是异步任务,内部代码在打印完 p2 后才执行,所以在打印 p2 时,Promise 还没执行 resolve() ,状态是 pending。
  • 打印完 p2 后,setTimeout 内的 resolve() 执行,Promise 的状态变为 resolved

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Promise.resolve 是一个在 JavaScript 中用来创建一个已经解决(resolved)的 Promise 对象的方法。它返回一个表示解决结果的 Promise 对象。 当传递给 Promise.resolve 的参数是一个包 then 方法的对象时(称为 thenable 对象),Promise.resolve 方法会将该对象转换为一个 Promise 对象,并将其解决结果作为该 Promise 对象的解决值。如果传递给 Promise.resolve 的参数已经是一个 Promise 对象,则它会直接返回该对象,不做任何处理。 以下是 Promise.resolve 的示例用法: ```javascript // 创建一个已解决的 Promise 对象 const resolvedPromise = Promise.resolve("Resolved Value"); resolvedPromise.then(value => { console.log(value); // 输出 "Resolved Value" }); // 创建一个已解决的 Promise 对象,通过 thenable 对象 const thenable = { then(resolve) { resolve("Thenable Resolved Value"); } }; const resolvedThenable = Promise.resolve(thenable); resolvedThenable.then(value => { console.log(value); // 输出 "Thenable Resolved Value" }); // 返回传入的 Promise 对象 const existingPromise = new Promise(resolve => { resolve("Existing Promise Resolved Value"); }); const resolvedExistingPromise = Promise.resolve(existingPromise); resolvedExistingPromise.then(value => { console.log(value); // 输出 "Existing Promise Resolved Value" }); ``` 解释: Promise.resolve 方法的作用是将一个值转换为一个已解决的 Promise 对象。如果传入的值是一个 Promise 对象,那么 Promise.resolve 方法会直接返回该对象。如果传入的值是一个 thenable 对象,它会转换为一个 Promise 对象,并将其解决结果作为解决值。如果传入的值是一个非 thenable 对象,它会被包装在一个新的 Promise 对象中,并将其作为解决值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朝阳39

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值