Promise

Promise概念

Promise是异步编程的一种解决方案。

什么是Promise

// Promise代表一个异步操作的最终结果
// 三种状态:pending、fulfilled、rejected

const promise = new Promise(function(resolve, reject) {
    // 异步操作
    setTimeout(function() {
        const success = true;
        
        if (success) {
            resolve("操作成功");  // 状态变为fulfilled
        } else {
            reject("操作失败");   // 状态变为rejected
        }
    }, 1000);
});

console.log(promise);  // Promise { <pending> }

Promise状态

// Promise有三种状态
// 1. pending: 初始状态,既不是成功也不是失败
// 2. fulfilled: 操作成功完成
// 3. rejected: 操作失败

// 状态只能改变一次
const p1 = new Promise(function(resolve, reject) {
    resolve("成功");
    reject("失败");  // 无效,状态已改变
});

// 状态不可逆
const p2 = new Promise(function(resolve, reject) {
    resolve("第一次");
    resolve("第二次");  // 无效
});

创建Promise

创建Promise实例的方法。

new Promise

// 基本创建
const promise = new Promise(function(resolve, reject) {
    // 执行异步操作
    fetch("/api/data")
        .then(response => response.json())
        .then(data => resolve(data))
        .catch(error => reject(error));
});

// 简化版
const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        const random = Math.random();
        if (random > 0.5) {
            resolve(random);
        } else {
            reject(new Error("随机数太小"));
        }
    }, 1000);
});

Promise.resolve

// 创建已解决的Promise
const p1 = Promise.resolve("成功");
p1.then(value => console.log(value));  // "成功"

// 等价于
const p2 = new Promise(resolve => resolve("成功"));

// 传递Promise会原样返回
const p3 = Promise.resolve(p1);
console.log(p1 === p3);  // true

// 传递thenable对象
const thenable = {
    then(resolve) {
        resolve("thenable");
    }
};
const p4 = Promise.resolve(thenable);
p4.then(value => console.log(value));  // "thenable"

Promise.reject

// 创建已拒绝的Promise
const p1 = Promise.reject("失败");
p1.catch(reason => console.log(reason));  // "失败"

// 等价于
const p2 = new Promise((_, reject) => reject("失败"));

// 注意:reject不会解析Promise
const p3 = Promise.reject(Promise.resolve("成功"));
p3.catch(reason => console.log(reason));  // Promise { "成功" }

then方法

处理Promise成功的结果。

基本用法

const promise = Promise.resolve("东巴文");

promise.then(function(value) {
    console.log(value);  // "东巴文"
});

// then返回新的Promise
const p1 = Promise.resolve(1);
const p2 = p1.then(value => value + 1);
const p3 = p2.then(value => value * 2);

p3.then(value => console.log(value));  // 4

链式调用

// then返回新Promise,支持链式调用
fetch("/api/user/1")
    .then(response => response.json())
    .then(user => fetch(`/api/posts/${user.id}`))
    .then(response => response.json())
    .then(posts => console.log(posts))
    .catch(error => console.error(error));

// 返回值会自动包装成Promise
Promise.resolve(1)
    .then(value => value + 1)       // 返回2
    .then(value => value * 2)       // 返回4
    .then(value => {
        console.log(value);         // 4
        return value + 1;
    })
    .then(value => console.log(value));  // 5

返回Promise

// then回调返回Promise
Promise.resolve(1)
    .then(value => {
        return new Promise(resolve => {
            setTimeout(() => resolve(value * 2), 1000);
        });
    })
    .then(value => {
        console.log(value);  // 2(等待1秒后输出)
        return value + 1;
    })
    .then(value => console.log(value));  // 3

catch方法

处理Promise失败的情况。

基本用法

const promise = Promise.reject(new Error("出错了"));

promise.catch(function(error) {
    console.error(error.message);  // "出错了"
});

// 等价于then的第二个参数
promise.then(
    null,
    function(error) {
        console.error(error);
    }
);

错误冒泡

// 错误会沿着链向下传递
Promise.resolve()
    .then(() => {
        throw new Error("第一步出错");
    })
    .then(() => {
        console.log("不会执行");
    })
    .then(() => {
        console.log("不会执行");
    })
    .catch(error => {
        console.error(error.message);  // "第一步出错"
    });

catch后继续

// catch后可以继续then
Promise.reject("错误")
    .catch(error => {
        console.log("捕获:", error);
        return "恢复";
    })
    .then(value => {
        console.log("继续:", value);  // "继续: 恢复"
    });

// catch后再次抛出错误
Promise.reject("错误1")
    .catch(error => {
        console.log("捕获:", error);
        throw new Error("错误2");
    })
    .catch(error => {
        console.log("再次捕获:", error.message);  // "错误2"
    });

finally方法

无论成功失败都会执行。

基本用法

const promise = fetch("/api/data");

promise
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error))
    .finally(() => {
        console.log("请求完成");
        // 隐藏加载动画
        hideLoading();
    });

finally特点

// finally不接收参数
Promise.resolve("成功")
    .finally(() => {
        console.log("清理");
    })
    .then(value => {
        console.log(value);  // "成功"
    });

// finally返回值会被忽略
Promise.resolve(1)
    .finally(() => 2)
    .then(value => console.log(value));  // 1

// finally抛出的错误会传递
Promise.resolve(1)
    .finally(() => {
        throw new Error("finally错误");
    })
    .catch(error => console.log(error.message));  // "finally错误"

Promise链式调用

多个Promise按顺序执行。

链式调用原理

// then返回新的Promise
const p1 = Promise.resolve(1);

const p2 = p1.then(value => {
    console.log("p1:", value);
    return value + 1;
});

const p3 = p2.then(value => {
    console.log("p2:", value);
    return value + 1;
});

p3.then(value => {
    console.log("p3:", value);
});

// 输出:
// p1: 1
// p2: 2
// p3: 3

链式调用示例

// 用户登录流程
function login(username, password) {
    return fetch("/api/login", {
        method: "POST",
        body: JSON.stringify({ username, password })
    })
    .then(response => response.json())
    .then(data => {
        if (!data.success) {
            throw new Error(data.message);
        }
        return data.token;
    });
}

function getUserInfo(token) {
    return fetch("/api/user", {
        headers: { Authorization: `Bearer ${token}` }
    })
    .then(response => response.json());
}

function getOrders(userId) {
    return fetch(`/api/orders/${userId}`)
    .then(response => response.json());
}

// 链式调用
login("东巴文", "password")
    .then(token => {
        localStorage.setItem("token", token);
        return getUserInfo(token);
    })
    .then(user => {
        console.log("用户:", user);
        return getOrders(user.id);
    })
    .then(orders => {
        console.log("订单:", orders);
    })
    .catch(error => {
        console.error("错误:", error);
    });

Promise.all

并行执行多个Promise。

基本用法

// 所有Promise都成功才成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3])
    .then(values => {
        console.log(values);  // [1, 2, 3]
    });

// 一个失败就失败
const p4 = Promise.reject("错误");

Promise.all([p1, p2, p4])
    .then(values => {
        console.log("不会执行");
    })
    .catch(error => {
        console.error(error);  // "错误"
    });

实际应用

// 并行请求多个接口
function loadPageData() {
    return Promise.all([
        fetch("/api/user").then(r => r.json()),
        fetch("/api/posts").then(r => r.json()),
        fetch("/api/comments").then(r => r.json())
    ]);
}

loadPageData()
    .then(([user, posts, comments]) => {
        console.log("用户:", user);
        console.log("文章:", posts);
        console.log("评论:", comments);
    })
    .catch(error => {
        console.error("加载失败:", error);
    });

// 带超时的请求
function fetchWithTimeout(url, timeout = 5000) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) => {
            setTimeout(() => reject(new Error("请求超时")), timeout);
        })
    ]);
}

Promise.race

返回最先完成的Promise。

基本用法

// 返回最先改变状态的Promise
const p1 = new Promise(resolve => {
    setTimeout(() => resolve("慢"), 3000);
});

const p2 = new Promise(resolve => {
    setTimeout(() => resolve("快"), 1000);
});

Promise.race([p1, p2])
    .then(value => {
        console.log(value);  // "快"
    });

超时实现

function fetchWithTimeout(url, timeout = 5000) {
    const fetchPromise = fetch(url);
    const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => reject(new Error("请求超时")), timeout);
    });
    
    return Promise.race([fetchPromise, timeoutPromise]);
}

fetchWithTimeout("/api/data", 3000)
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

Promise.allSettled

等待所有Promise完成(无论成功失败)。

基本用法

const p1 = Promise.resolve("成功");
const p2 = Promise.reject("失败");
const p3 = Promise.resolve("成功2");

Promise.allSettled([p1, p2, p3])
    .then(results => {
        console.log(results);
        // [
        //   { status: "fulfilled", value: "成功" },
        //   { status: "rejected", reason: "失败" },
        //   { status: "fulfilled", value: "成功2" }
        // ]
    });

处理结果

Promise.allSettled([
    fetch("/api/user").then(r => r.json()),
    fetch("/api/posts").then(r => r.json())
])
.then(results => {
    results.forEach((result, index) => {
        if (result.status === "fulfilled") {
            console.log(`请求${index}成功:`, result.value);
        } else {
            console.error(`请求${index}失败:`, result.reason);
        }
    });
});

Promise.any

返回第一个成功的Promise。

基本用法

const p1 = Promise.reject("错误1");
const p2 = new Promise(resolve => {
    setTimeout(() => resolve("成功"), 1000);
});
const p3 = Promise.reject("错误2");

Promise.any([p1, p2, p3])
    .then(value => {
        console.log(value);  // "成功"
    })
    .catch(error => {
        console.error(error);  // AggregateError
    });

// 所有都失败才会失败
Promise.any([
    Promise.reject("错误1"),
    Promise.reject("错误2")
])
.catch(error => {
    console.error(error);  // AggregateError: All promises were rejected
    console.log(error.errors);  // ["错误1", "错误2"]
});

下一步

掌握了Promise后,让我们继续学习:

  1. Async/Await - 学习async/await语法
  2. 异步迭代 - 学习异步迭代器
  3. Ajax - 学习Ajax请求

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:Promise是现代JavaScript异步编程的核心,掌握Promise的创建、链式调用和组合方法,可以优雅地处理复杂的异步流程。在 db-w.cn,我们帮你彻底理解Promise!