为什么需要使用 Promise ?
传统回调函数的代码层层嵌套,形成回调地狱,难以阅读和维护,为了解决回调地狱的问题,更加优雅地书写复杂的异步任务,诞生了 Promise
什么是 Promise ?
Promise 是一种异步编程的解决方案,本身是一个构造函数
console.log(Promise); // [Function: Promise]
自带resolve,reject,all 等方法,其原型上还有then、catch等方法。
Promise 的三种状态及其变化
- pending 进行中,不会触发 then 和 catch 回调函数
- resolved / fulfilled 已成功,会触发后续的 then 回调函数
- 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 实例的状态变为 reject
const p1 = new Promise((resolve, reject) => { reject() })
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