Promise

66 阅读2分钟

介绍

  • Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。— MDN
  • 是用于处理异步操作的核心工具

出现原因

  • 异步操作更直观,避免嵌套回调
// Promise 出现之前,我们使用事件和回调
const loadScript = (src, callback) => {
    // 创建一个 <script> 标签, 并将其添加到页面
    let script = document.createElement("script");
    script.src = src;

    script.onload = () => callback(null, script);
    script.onerror = () => callback(new Error(`Script load error for ${src}`));
    document.head.append(script);
};

// 此处只加载一个脚本文件,回调看起来还比较清晰,
loadScript(
    "https://unpkg.com/vue@3/dist/vue.global.js",
    function (error, script) {
        if (error) {
            console.log(error);
        } else {
            console.log("loading Vue"); // "loading Vue"
        }
    }
);

// 但是如果我们想加载多个脚本文件呢?如下所示:
loadScript(
    "https://unpkg.com/vue@3/dist/vue.global.js",
    function (error, script) {
        console.log("loading Vue"); // "loading Vue"

        // 嵌套回调
        loadScript(
            "https://unpkg.com/react@18/umd/react.development.js",
            function (error, script) {
                console.log("loading React"); // "loading React"
            }
        );
    }
);
  • 使用 promise 避免嵌套回调
const loadScript = (src) => {
    return new Promise(function (resolve, reject) {
        let script = document.createElement("script");
        script.src = src;

        script.onload = () => resolve(script);
        script.onerror = () => reject(new Error(`Script load error for ${src}`));

        document.head.append(script);
    });
};

// .then 链式调用 
loadScript("https://unpkg.com/vue@3/dist/vue.global.js")
    .then((script) => {
        console.log("loading Vue"); // "loading Vue"
        return loadScript("https://unpkg.com/react@18/umd/react.development.js");
    })
    .then((script) => console.log("loading React")) // "loading React"
    .catch((error) => console.log(error));

创建 Promise

  • 通过 new Promise() 构造函数创建,接收一个执行器函数(executor
const myPromise = new Promise((resolve, reject) => {
  // 异步操作(如 API 请求、定时器等)
  if (/* 成功条件 */) {
    resolve("Success!"); // 状态变为 fulfilled
  } else {
    reject("Error!"); // 状态变为 rejected
  }
});

状态说明

  • new Promise 构造器返回的 pending 对象具有以下内部属性:
    • state:
      • 初始值是 pending
      • resolve 调用后变为 fulfilled,
      • reject 调用之后变为 rejected
    • result: -初始值是 undefined
      • resolve(value) 调用后变为 value
      • reject(error) 调用时变为 error
  • 状态一旦改变不可逆
new Promise(() => {}) // 返回 Promise {<pending>}
new Promise((resolve) => { resolve('testB') }) // Promise {<fulfilled>: testB}
new Promise((reject) => { resolve('testC') }) // Promise {<rejected>: testC}

链式调用

  • Promise 支持链式调用,.then.catch.finally 方法均支持链式调用。
    • 其中,.then 方法接收两个参数
      • 第一个参数处理已决议状态(fulfilled)的回调函数
      • 第二个参数则处理已经拒绝(rejected)的回调函数。
    • 每一个 .then() 方法返回的是一个新生成的 Promise 对象,这个对象可被用作链式调用。
var promiseResolve = new Promise((resolve, reject) => resolve(2));
promiseResolve.then(
    res => console.log('resolve', res), 
    res => console.log('reject', res)
);
// resolve 2

// 单独处理错误
var PromiseReject = new Promise((resolve, reject) => reject(3));
PromiseReject.then(
    res => console.log('resolve', res), 
    res => console.log('reject', res)
);
// reject 3

// rejected 回调和 .catch同时使用,仅触发rejected回调
PromiseReject
.then(
    res => console.log('resolve', res),
    res => console.log('reject', res)
).catch(
    err => console.log('catch', err)
);
// reject 3

// Promise 的`.then()`方法会返回一个新的 Promise,这使得可以进行链式调用
new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
})
.then((value) => {
    console.log(value); // 输出: 1
    return value * 2;
})
.then((newValue) => {
    console.log(newValue); // 输出: 2
    return newValue + 3;
})
.then((finalValue) => {
    console.log(finalValue); // 输出: 5
})
.catch((error) => {
    console.log(error);
});

// 使用catch自动捕获链条上的所有Promise抛出的错误
PromiseReject
.then((result) => {
    console.log(result); // 成功时执行(接收 resolve 的值)
})
.catch((error) => {
    console.error(error); // 失败时执行(接收 reject 的值)
})
.finally(() => {
    console.log("无论成功/失败都会执行");
});

// 执行顺序

console.log(1)
new Promise((resolve) => {
    // 立即执行
    console.log(2)
    resolve(4)
}).then(res => {
    // 异步执行
    console.log(res)
})
console.log(3)
// 返回 1 2 3 4 5

Promise.resolve() / Promise.reject()

  • 快速创建已解决/已拒绝的 Promise
Promise.resolve("立即成功").then(console.log); // 输出 "立即成功"
Promise.reject("立即失败").catch(console.error); // 输出 "立即失败"

与 async/await 结合

  • Promise 是 async/await 的底层机制:
async function fetchData() {
  try {
    const result = await myPromise; // 等待 Promise 解决
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}