promise 规范应用

promise 规范及应用

Promises / PromiseA+ 规范

PromiseA+ 规范

  • promise:一个标准、规范
  • 三种状态:pendingfulfilledreject,状态一旦改变不允许再修改
js 复制代码
let promise_0 = new Promise((resolve) => {
    setTimeout(() => {
        resolve("promise_0");
    }, 100);
});

let promise_1 = Promise.resolve(promise_0);
// promise_1 会在  promise_0 fulfilled时,自动以同样的值 fulfilled

let thenable = {
    then: function (resolve, reject) {
        setTimeout(() => resolve("success"), 100);
    },
};
let promise_2 = Promise.resolve(thenable);
// promise_2 最终 fulfilled,值为 "success"
  1. 状态

    • Promise 必须从 pending 转为 fulfilled 或 rejected,且一旦转换之后,状态和值(或原因)就固定下来,不会再变
    • 例如:一个异步操作,在成功时调用 resolve 并传入数据,在出错时候调用 reject 并传入错误原因
  2. then 方法

    • then 接受两个回调,分别用于处理成功和失败

    • 如果回调返回值,则将值传递给下一个 then,如果抛出异常,则下一个 then 会接收到拒绝原因

      js 复制代码
      someAsyncOperation().then((value) => {
       console.info("success", value);
       return value + 1;
      }).then((value) => {
       console.info("链式调用", value);
      }).catch((reason) => {
       console.error("error", reason
      })
  3. Promise 解析过程

    • 如果一个 then 回调返回了另一个 Promise,那么当前 Promise 就会 "跟随"这个返回的 Promise 状态,并且采用它的值

传统异步任务的处理

  • 回调函数

    • 一个简单的请求:

      • 调用一个函数,这个函数中返回网络请求
      • 发送网络请求成功,就告知调用者成功,并返回数据
      • 发送网络请求失败,就告知调用者失败,并返回失败信息
    js 复制代码
    function request(cb) {
        // 模拟发送网络请求
        let flag = Math.random() > 0.5 ? true : false;
        setTimeout(() => {
          cb(flag ? "success" : "fail"
        },500)
    }
    // ...
    console.info("request: 发起请求")
    request((status,msg)=>{
        console.info("request: ",status,msg)
    })
    // 回调函数
    request((s1,m1)=>{
        // 业务处理逻辑1
        request((s2,m2)=>{
          // 业务处理逻辑2
            request((s3,m3)=>{
            // 业务处理逻辑3
                console.info("request: ",s3,m3)
        })
    })
  • 回调地狱

    • 如果是自己封装的请求方法,必须设计好规范,如果使用他人库,则必须要通过查看文档或源码才知道如何使用这个函数
    • 及其容易出现回调地狱

Promise

  1. 什么是 Promise
  • 是一个类,用来封装异步操作并获取其成功或失败的结果

  • 当通过 new 创建 Promise 实例,需要传入一个回调函数,我们称之为 executor

    • 这个回调函数会被立即执行,并传入两个参数 resolve 和 reject
    • 当调用 resolve 回调函数时,会执行 Promise 对象的 then 方法传入的回调
    • 当调用 reject 回调函数时,会执行 Promise 对象的 catch 方法传入的回调
  • Promise 是一个状态机,分为 pending、fulfilled、rejected 三个状态

    • pending:初始状态,执行了 executor 后,处于该状态
    • fulfilled:成功状态,调用 resolve() 后,Promise 的状态变为 fulfilled,且无法再改变
    • rejected:失败状态,调用 reject() 后,Promise 的状态变为 rejected,且无法再改变
js 复制代码
function request(cb) {
    // 模拟发送网络请求
    let flag = Math.random() > 0.5 ? true : false;
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (flag) {
                resolve("success");
                return;
            }
            reject("error");
        });
    }, 500);
}
console.info("request: 发起请求");
request().then(
    (msg) => console.info("request: ", msg),
    (err) => console.error("request: ", err)
);
  1. resolve 的参数
  • 如果传入的是普通的值或者对象,则会传递到 then 的参数中
  • 如果传入的是一个 Promise,那么当前的 Promise 状态会由传入的 Promise 决定
js 复制代码
const newPromise = new Promise((resolve, reject) => {
    resolve("success");
});

new Promise((resolve, reject) => {
    // 当前 Promise 的状态由 newPromise 决定
    resolve(newPromise);
})
    .then((res) => console.info(res)) // success
    .catch((err) => console.error(err));
  • 如果传入的是一个对象,并且该对象实现了 then 方法 (thenable),也会执行该 then 方法,并由该 then 方法决定后续的状态
js 复制代码
new Promise((resolve, reject) => {
    resolve({
        then(resolve, reject) {
            reject("error");
        },
    });
}).then(
    (res) => console.info(res),
    (err) => console.error(err) // error
);
  1. Promise 的实例方法
  • then 方法:通过 then 方法可以对 Promise 的 resolve 进行处理,then 方法的返回值是一个 Promise 实例

    • 多次调用 then 方法:同一个 Promise 实例可以多次调用 then 方法,当 Promise 中 resolve 被回调时,所有 then 方法传入的传入的函数都会被调用
js 复制代码
const promise_0 = new Promise((resolve, reject) => {
    resolve("success");
});
// 同时调用
promise_0.then((res) => console.info(res));
promise_0.then((res) => console.info(res));
promise_0.then((res) => console.info(res));
  • then 方法传入的回调函数可以有返回值

    • 如果返回的是普通值,那么这个普通值作为一个新的 Promise 的 resolve 的值
js 复制代码
const promise_1 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_1.then(() => "then").then((res) => console.info(res)); // then

// promise_1.then(() => "then") 等价于
promise_1.then(() => {
    return new Promise((resolve) => {
        resolve("then");
    });
});
  • 如果返回的是 Promise,那么就可以再次调用 then 方法
  • 如果返回的是一个对象,并且该对象实现了 thenable,该 then 函数有两个参数 resolve,reject,则 resolve 会传递给下一个 Promise
js 复制代码
const promise_2 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_2
    .then(() => {
        return {
            then(resolve) {
                return resolve("then"); // resolve 传递给下一个 Promise
            },
        };
    })
    .then((res) => console.info(res)); // then
  • catch 方法

    • 除了 then 方法的第二个参数来捕获 reject 之外, catch 方法用于处理 Promise 的 reject,catch 方法返回的也是一个 Promise 实例
js 复制代码
const promise_3 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_3.then((undefined, err) => {
    // 打印 err
    console.error(err);
});
// 这种写法不太符合 `promise/A+ 规范`
promise_3.catch((err) => {
    // 打印 err
    console.error(err);
});
// 符合规范的写法
promise_3.then(
    () => {},
    (err) => {
        console.error(err);
    }
);
  • catch 方法也是可以多次调用的,只要 Promise 实例的状态是 reject,那么就会调用 catch 方法

  • finally 方法

    • 无论一个 Promise 实例是 fulfilled 还是 rejected,finally 方法都会被调用。finally 方法不接收参数,返回的也是一个 Promise 实例
js 复制代码
const promise_4 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_4
    .then(() => {})
    .catch((err) => console.error(err))
    .finally(() => console.info("finally code execute"));
  • Promise 的类方法
  1. resolve 方法
js 复制代码
const foo = { name: "Fa-ce" };
function bar(obj) {
    return new Promise((resolve) => {
        resolve(obj);
    });
}
bar(foo).then((res) => console.info(res)); // { name: 'Fa-ce' }
  • resolve 参数:

    • 参数本身是 Promise
    • 参数是原始值/对象
    • 参数是一个 thenable
  1. reject 方法
  2. all 方法
  3. race 方法
  4. allSettled 方法
  5. any 方法
  • all 方法:接收一个 Promise[],返回一个 Promise 实例,当所有的 Promise 执行完毕且都是 fulfilled 状态时,该实例的状态才会变成 fulfilled,只要队列中有一个实例的状态是 rejected,那么该实例的状态就会变成 rejected

    • 如果 Promise 队列中所有的实例状态都是 fulfilled,那么 Promise.all() 方法返回的实例状态就会变成 fulfilled,并且 then() 的参数使用给数组,按照顺序放置队列中每个 Promise 成功后的结果
js 复制代码
let i = 0;
function genPromise() {
    return new Promise((resolve) => {
        resolve(`success${i++}`);
    });
}

const promiseArr = [genPromise(), genPromise(), genPromise()];
Promise.all(promiseArr).then((res) => console.info(res)); // ['success0', 'success1', 'success2']
  • 如果队列中 Promise 实例有一个是 rejected,那么 Promise.all() 方法返回的实例状态就会变成 rejected,并且 reject() 的参数使用给第一个 rejected 的结果
js 复制代码
let i = 0;
function genPromise() {
    return new Promise((resolve) => {
        resolve(`success${i++}`);
    });
}
const promiseArr = [
    genPromise(),
    new Promise((resolve, reject) => reject("error1")),
    new Promise((resolve, reject) => reject("error2")),
    genPromise(),
];
Promise.all(promiseArr).catch((err) => console.info(err)); // error1
  • allSettled 方法:该方法返回的 Promise 实例,会在所有 Promise 实例执行完毕后,状态变成 fulfilled,并且只会是 fulfilled

    • 无论队列中的 Promise 实例的状态如何,都能取到结果
    • 打印的结果,会包含状态以及值/原因
js 复制代码
let i = 0;
function genPromise() {
    return new Promise((resolve) => {
        resolve(`success${i++}`);
    });
}
const promiseArr = [
    genPromise(),
    new Promise((resolve, reject) => reject("error1")),
    new Promise((resolve, reject) => reject("error2")),
    genPromise(),
];
Promise.allSettled(promiseArr)
    .then((res) => console.info(res))
    .catch((err) => console.info(err));
  • race 方法

    • Promise.race() 方法接收一个Promise数组,返回一个Promise实例,只要数组中的某个Promise实例状态改变,那么返回的Promise实例状态就会改变,并且使用第一个改变状态的Promise实例的值/原因
    • 返回最先执行完的Promise实例
js 复制代码
const promiseArr = [
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("success");
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("error");
        }, 1500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("success");
        }, 600);
    }),
];
Promise.race(promiseArr)
    .then((res) => console.info(res)) // success
    .catch((err) => console.info(err));
  • any 方法

    • Promise.any() 方法会等待一个fulfilled状态,才会决定返回的Promise实例的状态
    • 如果队列中所有的实例都是reject,那么也需要等所有执行完毕后才返回,并且返回的实例状态是rejected,并且使用第一个reject的值/原因
js 复制代码
const promiseArr = [
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("success");
            // reject("reject-0");
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("error");
        }, 1500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve("success");
            reject("reject_2");
        }, 600);
    }),
];

Promise.any(promiseArr)
    .then((res) => console.info(res))
    .catch((err) => console.info(err));
// 遇到第一个 fulfilled时,转变返回的 Promise 实例状态
// 如果所有都是 rejected,那么只有所有执行完毕后,才会返回

Promise 回调和 async/await

js 复制代码
Promise.resolve("green").then((result) => {
    console.log(result);

    Promise.resolve("red").then((result) => {
        console.log(result);

        Promise.resolve("yellow").then((result) => {
            console.log(result);
        });
    });
});
js 复制代码
async () => {
    await Promise.resolve("green").then((res) => {
        console.log(res);
    });
    await Promise.resolve("red").then((res) => {
        console.log(res);
    });
    await Promise.resolve("yellow").then((res) => {
        console.log(res);
    });
};
// 浏览器不直接支持 async/await,需要 babel 转译
  • async 是 function 的一个前缀,只有 async 函数中才能使用 await 语法
  • async 函数是一个 Promise 对象,有无 resolve 取决于有无函数中 return 值
  • await 后边跟得是一个 Promise 对象,如果不是,则会包裹一层 Promise.resolve(),将其转化为 Promise 对象

await 只能在 async 函数中使用,不能在普通函数中使用,它会暂停函数执行, 直到 等待的 Promise 成功或者失败

js 复制代码
async function fetchData() {
    return "Data fetched successfully";
}

async function showData() {
    const data = await fetchData();
    console.log(data);
}

/* ************ */
async function processData() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (err) {
        console.log(err);
    }
}
js 复制代码
async function processSequentAll() {
    const p1 = await fetchData1();
    const p2 = await fetchData2(data2);
    const p3 = await fetchData3(data3);
}
// 或者
async function processSequentAll() {
    const p1 = fetchData1();
    const p2 = fetchData2(data2);
    const p3 = fetchData3(data3);

    const result = await Promise.all([p1, p2, p3]);
    console.log(result);
}
  • 从 async/await 到 promise
js 复制代码
function fetchDataAsPromise() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("data fetched successfully");
        }, 1000);
    });
}
// 从 promise 到 async
async function useFetchDataAsPromise() {
    const data = await fetchDataAsPromise();
    console.log(data);
}
useFetchDataAsPromise();
相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax