【JavaScript】深入理解Promise:从基础概念到进阶用法、手写promise

在这里插入图片描述

🔥 个人主页:空白诗

在这里插入图片描述

文章目录

    • 一、引言
    • 二、Promise概述
      • 1. Promise的定义
      • 2. Promise的用途
      • 3. Promise的三种状态
      • 4. Promise的构造函数和基础结构
      • 5. Promise的优点
      • 6. Promise的实例方法
      • 7. Promise的静态方法
    • 三、Promise的基本用法
      • 1. 创建一个Promise
      • 2. `then`方法
      • 3. `catch`方法
      • 4. `finally`方法
      • 5. 链式调用
      • 6. 示例代码
    • 四、Promise的进阶用法
      • 1. `Promise.all`
      • 2. `Promise.race`
      • 3. `Promise.allSettled`
      • 4. `Promise.any`
      • 5. 使用示例
    • 五、如何手写一个简单的Promise
      • 1. 实现Promise的基本结构
      • 2. 实现`then`方法
      • 3. 实现`catch`方法
      • 4. 实现`finally`方法
      • 5. 完整的Promise实现
    • 六、Promise的实际应用示例
      • 1. 异步数据加载
      • 2. 多个异步任务的并行处理
      • 3. 顺序执行多个异步任务
      • 4. 处理并发的异步任务
    • 七、总结

在这里插入图片描述

一、引言

在现代Web开发中,异步编程是不可避免的。无论是发起网络请求、读取文件、定时操作,还是处理事件,异步操作都无处不在。而在众多异步编程解决方案中,Promise因其简洁易用、链式调用和更好的错误处理机制,成为了开发者们的首选。

Promise是一种用于处理异步操作的JavaScript对象,它代表了一个在未来某个时间点才会完成(或失败)的操作及其结果。相比于传统的回调函数,Promise提供了一种更加优雅和便捷的方式来处理异步任务,从而避免了“回调地狱”的问题。

本文将详细讲解Promise的概念、基本用法和进阶用法,并最终手写一个简单的Promise实现。通过这篇文章,读者将不仅能够深入理解Promise的工作原理,还能学会如何在实际项目中有效地使用Promise处理异步操作。


二、Promise概述

1. Promise的定义

Promise是JavaScript中的一种对象,用于表示一个在未来某个时间点才会完成(或失败)的异步操作及其结果。它提供了一种处理异步操作的统一接口,使代码更加简洁和易于理解。与传统的回调函数相比,Promise能够更好地处理异步操作的结果和错误。

2. Promise的用途

Promise广泛用于处理各种异步操作,特别是在以下场景中:

  • 网络请求:使用fetchXMLHttpRequest进行Ajax调用。
  • 文件操作:读取文件、写入文件等I/O操作。
  • 定时操作:使用setTimeoutsetInterval进行延时操作。
  • 事件处理:处理用户事件,如点击、输入等。
  • 数据库操作:执行数据库查询、插入、更新等操作。

通过使用Promise,开发者可以避免嵌套的回调函数(即“回调地狱”),从而使代码更加线性和易读。

3. Promise的三种状态

Promise有三种状态:

  1. Pending(待定):初始状态,操作尚未完成。
  2. Fulfilled(已完成):操作成功完成,并有一个结果值。
  3. Rejected(已拒绝):操作失败,并有一个失败原因。

一个Promise对象只能从Pending状态转变为Fulfilled状态或Rejected状态,并且状态一旦改变,就不能再改变。这种不可变性保证了Promise的可靠性和可预测性。

4. Promise的构造函数和基础结构

创建一个Promise实例需要传递一个执行函数,该函数接收两个参数:resolve和reject。这两个参数分别是函数,用于将Promise的状态从Pending变为Fulfilled或Rejected。

const promise = new Promise((resolve, reject) => {
    // 异步操作
    if (操作成功) {
        resolve(成功的结果);
    } else {
        reject(失败的原因);
    }
});

在这个基础结构中,resolvereject函数分别用于处理操作成功和失败的情况。当异步操作完成时,调用resolve将Promise的状态变为Fulfilled,调用reject将状态变为Rejected。

5. Promise的优点

Promise具有以下几个显著的优点:

  • 链式调用:可以通过链式调用then方法来处理异步操作的结果,使代码更加简洁。
  • 错误处理:通过catch方法统一处理错误,避免了多个回调函数中重复的错误处理代码。
  • 状态管理:Promise的状态一旦改变,就不能再变,这种不可变性保证了异步操作结果的可靠性。
  • 组合操作:Promise提供了多种组合方法,如Promise.allPromise.racePromise.allSettledPromise.any,使得处理多个异步操作变得更加方便。

6. Promise的实例方法

  • then方法:用于在Promise状态变为Fulfilled时,执行指定的回调函数,并返回一个新的Promise实例。
  • catch方法:用于在Promise状态变为Rejected时,执行指定的回调函数,并返回一个新的Promise实例。
  • finally方法:用于在Promise状态变为FulfilledRejected时,执行指定的回调函数,并返回一个新的Promise实例。
promise
    .then(result => {
        // 处理成功结果
    })
    .catch(error => {
        // 处理错误
    })
    .finally(() => {
        // 无论成功还是失败,都会执行的操作
    });

通过这些实例方法,开发者可以灵活地处理异步操作的结果和错误,并执行最终的清理操作。

7. Promise的静态方法

方法名描述示例代码
Promise.resolve返回一个状态为Fulfilled的Promise实例。Promise.resolve(42).then(value => console.log(value));
Promise.reject返回一个状态为Rejected的Promise实例。Promise.reject(new Error('Error')).catch(error => console.error(error));
Promise.all接受一个Promise数组,返回一个新的Promise实例。当所有Promise都变为Fulfilled时,状态变为Fulfilled;如果有任何一个Promise变为Rejected,状态变为RejectedPromise.all([promise1, promise2]).then(values => console.log(values)).catch(error => console.error(error));
Promise.race接受一个Promise数组,返回一个新的Promise实例。当第一个Promise变为FulfilledRejected时,状态立即变为FulfilledRejectedPromise.race([promise1, promise2]).then(value => console.log(value)).catch(error => console.error(error));
Promise.allSettled接受一个Promise数组,返回一个新的Promise实例。当所有Promise都变为FulfilledRejected时,状态变为FulfilledPromise.allSettled([promise1, promise2]).then(results => console.log(results));
Promise.any接受一个Promise数组,返回一个新的Promise实例。当任意一个Promise变为Fulfilled时,状态变为Fulfilled;如果所有Promise都变为Rejected,状态变为RejectedPromise.any([promise1, promise2]).then(value => console.log(value)).catch(error => console.error(error));

这些静态方法为处理多个异步操作提供了强大的工具,使得开发者可以根据具体需求选择合适的方法来组合Promise。

综上所述,Promise提供了一种简洁、高效、可维护的方式来处理异步操作,使得JavaScript的异步编程变得更加便捷和强大。


三、Promise的基本用法

1. 创建一个Promise

创建一个Promise实例时,需要传递一个执行函数,该函数接收两个参数:resolvereject。这两个参数分别是函数,用于将Promise的状态从Pending变为Fulfilled或Rejected。

const promise = new Promise((resolve, reject) => {
    // 异步操作
    if (操作成功) {
        resolve(成功的结果);
    } else {
        reject(失败的原因);
    }
});

2. then方法

then方法用于在Promise状态变为Fulfilled时,执行指定的回调函数。它接收两个参数:第一个是处理Fulfilled状态的回调函数,第二个是处理Rejected状态的回调函数(可选)。

promise.then(
    result => {
        console.log('操作成功:', result);
    },
    error => {
        console.error('操作失败:', error);
    }
);

3. catch方法

catch方法用于在Promise状态变为Rejected时,执行指定的回调函数。它是then方法的语法糖,专门用于处理Rejected状态。

promise
    .then(result => {
        console.log('操作成功:', result);
    })
    .catch(error => {
        console.error('操作失败:', error);
    });

4. finally方法

finally方法用于在Promise状态变为Fulfilled或Rejected时,执行指定的回调函数。无论Promise的最终状态如何,finally中的回调函数都会执行。

promise
    .then(result => {
        console.log('操作成功:', result);
    })
    .catch(error => {
        console.error('操作失败:', error);
    })
    .finally(() => {
        console.log('操作结束');
    });

5. 链式调用

Promise支持链式调用,这意味着可以在一个then方法之后继续调用另一个then方法。这使得处理多个异步操作变得更加方便。

promise
    .then(result => {
        console.log('第一次操作成功:', result);
        return 另一个Promise;
    })
    .then(result => {
        console.log('第二次操作成功:', result);
    })
    .catch(error => {
        console.error('操作失败:', error);
    });

6. 示例代码

以下是一个完整的示例代码,展示了如何创建一个Promise并使用thencatchfinally方法来处理异步操作的结果和错误。

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const success = Math.random() > 0.5;
            if (success) {
                resolve('操作成功');
            } else {
                reject('操作失败');
            }
        }, 1000);
    });
};

asyncOperation()
    .then(result => {
        console.log(result);
    })
    .catch(error => {
        console.error(error);
    })
    .finally(() => {
        console.log('操作结束');
    });

在这个示例中,asyncOperation函数返回一个Promise,它模拟了一个异步操作,并在1秒钟后根据随机数决定操作是成功还是失败。thencatchfinally方法分别用于处理操作的成功结果、错误和最终的清理工作。


四、Promise的进阶用法

1. Promise.all

Promise.all接受一个Promise数组,返回一个新的Promise实例。当所有Promise都变为Fulfilled时,新Promise的状态变为Fulfilled,并且它的结果是一个包含所有Promise结果的数组;如果有任何一个Promise变为Rejected,新Promise的状态变为Rejected,并且它的结果是第一个被拒绝的Promise的原因。

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(values => {
    console.log(values); // [3, 42, "foo"]
}).catch(error => {
    console.error(error);
});

2. Promise.race

Promise.race接受一个Promise数组,返回一个新的Promise实例。当第一个Promise变为Fulfilled或Rejected时,新Promise的状态立即变为Fulfilled或Rejected,其结果就是第一个完成的Promise的结果。

const promise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then(value => {
    console.log(value); // "two"
}).catch(error => {
    console.error(error);
});

3. Promise.allSettled

Promise.allSettled接受一个Promise数组,返回一个新的Promise实例。当所有Promise都变为Fulfilled或Rejected时,新Promise的状态变为Fulfilled,并且它的结果是一个对象数组,每个对象表示对应的Promise的状态和结果或原因。

const promise1 = Promise.resolve('成功');
const promise2 = Promise.reject('失败');

Promise.allSettled([promise1, promise2]).then(results => {
    results.forEach((result) => {
        if (result.status === 'fulfilled') {
            console.log('成功:', result.value);
        } else {
            console.log('失败:', result.reason);
        }
    });
});

4. Promise.any

Promise.any接受一个Promise数组,返回一个新的Promise实例。当任意一个Promise变为Fulfilled时,新Promise的状态变为Fulfilled,并且它的结果就是第一个成功的Promise的结果;如果所有Promise都变为Rejected,新Promise的状态变为Rejected,并且它的结果是一个包含所有被拒绝原因的AggregateError对象。

const promise1 = Promise.reject('失败1');
const promise2 = Promise.reject('失败2');
const promise3 = Promise.resolve('成功');

Promise.any([promise1, promise2, promise3]).then(value => {
    console.log(value); // "成功"
}).catch(error => {
    console.error(error); // AggregateError: All promises were rejected
});

5. 使用示例

以下是一个综合示例,展示了如何使用Promise.allPromise.racePromise.allSettledPromise.any处理多个异步操作。

const fetchData1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, '数据1');
});

const fetchData2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 2000, '数据2');
});

const fetchData3 = new Promise((resolve, reject) => {
    setTimeout(reject, 1500, '数据3失败');
});

// 使用Promise.all
Promise.all([fetchData1, fetchData2, fetchData3])
    .then(values => {
        console.log('Promise.all结果:', values);
    })
    .catch(error => {
        console.error('Promise.all错误:', error);
    });

// 使用Promise.race
Promise.race([fetchData1, fetchData2, fetchData3])
    .then(value => {
        console.log('Promise.race结果:', value);
    })
    .catch(error => {
        console.error('Promise.race错误:', error);
    });

// 使用Promise.allSettled
Promise.allSettled([fetchData1, fetchData2, fetchData3])
    .then(results => {
        console.log('Promise.allSettled结果:', results);
    });

// 使用Promise.any
Promise.any([fetchData1, fetchData2, fetchData3])
    .then(value => {
        console.log('Promise.any结果:', value);
    })
    .catch(error => {
        console.error('Promise.any错误:', error);
    });

这个示例展示了如何使用不同的Promise静态方法来处理多个异步操作。fetchData1fetchData2fetchData3是三个模拟异步操作的Promise,通过不同的方法组合来展示它们的结果和错误处理。


五、如何手写一个简单的Promise

为了更深入理解Promise的工作原理,我们将手写一个简单的Promise实现,涵盖Promise的基本功能,包括状态管理、then方法、catch方法和finally方法。

1. 实现Promise的基本结构

首先,我们需要定义Promise的三种状态:PendingFulfilledRejected。接下来,我们定义一个Promise类,并在构造函数中初始化状态和结果。

class MyPromise {
    // 定义静态常量,表示Promise的三种状态
    static PENDING = 'pending'; // 待定态
    static FULFILLED = 'fulfilled'; // 已完成态
    static REJECTED = 'rejected'; // 已拒绝态

    constructor(executor) {
        this.state = MyPromise.PENDING; // 初始状态为待定态
        this.result = null; // 初始结果值为null
        this.callbacks = []; // 初始回调数组为空

        // 定义一个resolve函数,用于将Promise的状态改为已完成态
        const resolve = value => {
            if (this.state === MyPromise.PENDING) {
                this.state = MyPromise.FULFILLED;
                this.result = value;
                this.callbacks.forEach(callback => {
                    callback.onFulfilled(value);
                });
            }
        };

        // 定义一个reject函数,用于将Promise的状态改为已拒绝态
        const reject = reason => {
            if (this.state === MyPromise.PENDING) {
                this.state = MyPromise.REJECTED;
                this.result = reason;
                this.callbacks.forEach(callback => {
                    callback.onRejected(reason);
                });
            }
        };

        // 尝试执行executor函数,并传入resolve和reject作为参数
        try {
            executor(resolve, reject);
        } catch (error) {
            // 如果executor执行过程中抛出错误,则调用reject函数
            reject(error);
        }
    }

    // ...then, catch, finally 方法待实现
}

这段代码中,MyPromise类模拟了Promise的基本行为,包括创建Promise时传入的executor函数,以及resolve和reject两个函数用于改变Promise的状态。同时,还包含了处理executor执行过程中可能抛出的错误。

2. 实现then方法

then方法用于在Promise状态变为Fulfilled或Rejected时,执行指定的回调函数。我们需要处理同步和异步执行的情况,并返回一个新的Promise。

class MyPromise {
    // ...constructor 和其他代码

    then(onFulfilled, onRejected) {
        // 创建一个新的Promise实例并返回
        return new MyPromise((resolve, reject) => {
            // 定义一个处理回调的函数
            const handleCallback = (callback, state, result) => {
                try {
                    // 执行相应的回调函数,并获取其返回值
                    const value = callback(result);
                    // 如果返回值是一个Promise实例,则等待其解决或拒绝
                    if (value instanceof MyPromise) {
                        value.then(resolve, reject);
                    } else {
                        // 如果返回值不是Promise,则直接解决新的Promise
                        resolve(value);
                    }
                } catch (error) {
                    // 如果执行回调函数时抛出错误,则拒绝新的Promise
                    reject(error);
                }
            };

            // 根据当前Promise的状态,执行相应的回调函数
            if (this.state === MyPromise.FULFILLED) {
                // 如果当前Promise已成功,则异步执行onFulfilled回调
                setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0);
            } else if (this.state === MyPromise.REJECTED) {
                // 如果当前Promise已失败,则异步执行onRejected回调
                setTimeout(() => handleCallback(onRejected, this.state, this.result), 0);
            } else {
                // 如果当前Promise还处于待定状态,则将回调函数保存到callbacks数组中
                this.callbacks.push({
                    onFulfilled: () => setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0),
                    onRejected: () => setTimeout(() => handleCallback(onRejected, this.state, this.result), 0)
                });
            }
        });
    }

    // ...catch, finally 方法待实现
}

在这个实现中,then方法接受两个参数:onFulfilledonRejected,它们分别是Promise成功和失败时的回调函数。then方法会返回一个新的Promise实例。

如果当前Promise已经成功或失败,then方法会异步执行相应的回调函数,并根据回调函数的返回值来解决或拒绝返回的新的Promise。

如果当前Promise还处于待定状态,then方法会将回调函数保存到callbacks数组中,等待Promise状态改变时再执行。

使用setTimeout是为了确保回调函数的执行是异步的,这符合Promise的规范。

3. 实现catch方法

MyPromise类中,catch方法是一个用于处理Promise被拒绝(rejected)情况的便捷方法。它实际上是对then方法的简化调用,只关注拒绝情况的回调函数。因此可以说:catch方法是then方法的语法糖,只需调用then方法并将onRejected作为参数传递。

class MyPromise {
    // ...constructor, then method, 和其他代码

    catch(onRejected) {
        // 调用this.then方法,传入null作为成功的回调函数(因为不关心成功情况),
        // 并传入onRejected作为拒绝的回调函数。
        // 然后返回this.then方法的结果,它是一个新的Promise实例。
        return this.then(null, onRejected);
    }

    // ...finally方法待实现
}

在这个实现中,catch方法只接受一个参数onRejected,它是Promise被拒绝时的回调函数。catch方法会调用then方法,并传入null作为第一个参数(表示不关心Promise成功的情况),然后传入onRejected作为第二个参数。最后,catch方法会返回then方法的结果,即一个新的Promise实例。

这样,当Promise被拒绝时,catch方法提供的onRejected回调函数会被调用,并且任何在catch方法中返回的Promise都会成为catch方法返回的Promise的结果。如果onRejected回调函数本身返回一个Promise,那么catch方法返回的Promise将会等待这个返回的Promise解决或拒绝。

4. 实现finally方法

MyPromise类中,finally方法是一个用于指定不论Promise最终是fulfilled还是rejected,都会执行的操作的方法。它返回一个Promise。

class MyPromise {
    // ...constructor, then method, catch method, 和其他代码

    finally(onFinally) {
        // 调用this.then方法,传入两个回调函数:
        // 第一个回调函数处理fulfilled情况,调用onFinally()并执行完毕后返回value;
        // 第二个回调函数处理rejected情况,调用onFinally()并执行完毕后抛出reason。
        return this.then(
            value => {
                // 当Promise成功解决时执行
                onFinally(); // 执行finally中的回调函数
                return value; // 返回原始值,以便链式调用中的下一个then可以使用
            },
            reason => {
                // 当Promise被拒绝时执行
                onFinally(); // 执行finally中的回调函数
                throw reason; // 抛出拒绝的原因,以便链式调用中的下一个catch可以捕获
            }
        );
    }
}

在这个实现中,finally方法接受一个参数onFinally,它是一个没有参数的函数,表示无论Promise最终状态如何都需要执行的操作。finally方法通过调用then方法来实现,它传入两个回调函数:一个用于处理fulfilled情况,另一个用于处理rejected情况。在这两个回调函数中,都执行了onFinally(),以确保无论Promise的状态如何,onFinally都会被调用。然后,根据Promise的状态,返回相应的值或抛出相应的错误。

这样,使用finally方法可以让我们在Promise链的末尾添加一些清理操作,比如关闭文件、释放资源等,而不用担心这些操作会因为Promise的状态而改变。

5. 完整的Promise实现

以下是一个完整的Promise实现,包括状态管理、thencatchfinally方法。

// 定义一个简单的Promise类
class MyPromise {
    // 定义Promise的三种状态常量
    static PENDING = 'pending'; // 等待状态
    static FULFILLED = 'fulfilled'; // 成功状态
    static REJECTED = 'rejected'; // 失败状态

    // 构造函数,executor是执行器函数,接收resolve和reject两个参数
    constructor(executor) {
        this.state = MyPromise.PENDING; // 初始状态为pending
        this.result = null; // 存储Promise的结果值或原因
        this.callbacks = []; // 存储成功和失败的回调函数

        // 定义resolve函数,将Promise状态变为成功
        const resolve = value => {
            if (this.state === MyPromise.PENDING) { // 只有在pending状态时才能改变状态
                this.state = MyPromise.FULFILLED; // 将状态变为成功
                this.result = value; // 存储成功的结果值
                this.callbacks.forEach(callback => { // 执行所有成功的回调
                    callback.onFulfilled(value);
                });
            }
        };

        // 定义reject函数,将Promise状态变为失败
        const reject = reason => {
            if (this.state === MyPromise.PENDING) { // 只有在pending状态时才能改变状态
                this.state = MyPromise.REJECTED; // 将状态变为失败
                this.result = reason; // 存储失败的原因
                this.callbacks.forEach(callback => { // 执行所有失败的回调
                    callback.onRejected(reason);
                });
            }
        };

        // 执行传入的executor函数,传入resolve和reject函数,并捕获异常
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error); // 如果executor执行过程中抛出异常,直接调用reject
        }
    }

    // then方法,接收成功和失败的回调函数
    then(onFulfilled, onRejected) {
        // 返回一个新的Promise实例
        return new MyPromise((resolve, reject) => {
            // 处理回调函数的执行和状态传递
            const handleCallback = (callback, state, result) => {
                try {
                    const value = callback(result); // 执行回调函数获取返回值
                    if (value instanceof MyPromise) { // 如果返回值是Promise,等待其状态变化
                        value.then(resolve, reject);
                    } else {
                        resolve(value); // 否则直接将返回值作为新Promise的成功结果
                    }
                } catch (error) {
                    reject(error); // 捕获异常并将异常作为新Promise的失败结果
                }
            };

            if (this.state === MyPromise.FULFILLED) {
                // 如果当前Promise已经是fulfilled状态,异步执行onFulfilled回调
                setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0);
            } else if (this.state === MyPromise.REJECTED) {
                // 如果当前Promise已经是rejected状态,异步执行onRejected回调
                setTimeout(() => handleCallback(onRejected, this.state, this.result), 0);
            } else {
                // 如果当前Promise仍处于pending状态,将回调函数存储起来等待执行
                this.callbacks.push({
                    onFulfilled: () => setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0),
                    onRejected: () => setTimeout(() => handleCallback(onRejected, this.state, this.result), 0)
                });
            }
        });
    }

    // catch方法,用于捕获Promise链中的错误,等同于then(null, onRejected)
    catch(onRejected) {
        return this.then(null, onRejected);
    }

    // finally方法,用于在Promise执行结束后无论结果如何都会执行的回调
    finally(onFinally) {
        return this.then(
            value => {
                onFinally(); // 执行finally回调
                return value; // 继续传递成功的结果值
            },
            reason => {
                onFinally(); // 执行finally回调
                throw reason; // 继续传递失败的原因
            }
        );
    }
}

// 示例用法
const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        const random = Math.random();
        if (random < 0.5) {
            resolve('成功值');
        } else {
            reject('失败原因');
        }
    }, 1000);
});

promise.then(
    value => {
        console.log('成功:', value);
        return '处理后的值';
    },
    reason => {
        console.error('失败:', reason);
        throw new Error('处理失败');
    }
).then(
    value => {
        console.log('处理后的结果:', value);
    }
).catch(
    error => {
        console.error('捕获到的错误:', error.message);
    }
).finally(
    () => {
        console.log('无论如何都会执行的操作');
    }
);

成功情况:

成功: 成功值
处理后的结果: 处理后的值
无论如何都会执行的操作

失败情况:

失败: 失败原因
捕获到的错误: callback is not a function
无论如何都会执行的操作

通过这种方式,我们实现了一个简化版的Promise,涵盖了Promise的核心功能。这个实现可以帮助我们更好地理解Promise的工作原理和异步编程的机制。


六、Promise的实际应用示例

1. 异步数据加载

Promise常用于处理异步数据加载,例如从服务器获取数据并在页面上展示:

// 定义一个函数,用于从服务器获取数据
function fetchDataFromServer(url) {
    return new Promise((resolve, reject) => {
        // 使用fetch API发送请求
        fetch(url)
            .then(response => {
                // 检查网络响应是否成功
                if (response.ok) {
                    return response.json(); // 返回JSON数据
                }
                throw new Error('Network response was not ok.'); // 网络请求错误处理
            })
            .then(data => resolve(data)) // 将获取的数据传递给resolve
            .catch(error => reject(error)); // 捕获异常并传递给reject
    });
}

// 使用示例
fetchDataFromServer('https://api.example.com/data')
    .then(data => {
        console.log('成功获取数据:', data); // 打印成功获取的数据
        // 在页面上展示数据
    })
    .catch(error => {
        console.error('获取数据失败:', error); // 打印获取数据失败的错误信息
        // 处理错误情况
    });

这段代码定义了一个函数fetchDataFromServer,该函数接收一个URL作为参数,使用fetch API异步从该URL获取数据,并将获取的数据解析为JSON格式。

如果数据成功获取并解析,函数将通过Promise的resolve方法返回这些数据;如果在获取数据或解析数据过程中发生错误,函数将通过Promise的reject方法返回错误信息。这样,调用者可以使用.then().catch()方法来处理成功获取数据和获取数据失败的情况。

2. 多个异步任务的并行处理

Promise.all方法用于处理多个异步任务,等待所有任务完成后进行统一处理:

// 定义一个函数,模拟从服务器获取用户数据
const fetchUsers = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(['Alice', 'Bob', 'Charlie']), 1000); // 模拟异步获取用户数据
    });
};

// 定义一个函数,模拟从服务器获取文章数据
const fetchPosts = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(['Post 1', 'Post 2', 'Post 3']), 1500); // 模拟异步获取文章数据
    });
};

// 使用Promise.all处理多个异步任务
Promise.all([fetchUsers(), fetchPosts()])
    .then(([users, posts]) => {
        console.log('所有数据加载完成'); // 所有数据加载完成
        console.log('用户列表:', users); // 打印用户列表
        console.log('文章列表:', posts); // 打印文章列表
        // 在页面上展示用户和文章
    })
    .catch(error => {
        console.error('数据加载失败:', error); // 打印数据加载失败的错误信息
        // 处理错误情况
    });

这段代码定义了两个函数fetchUsersfetchPosts,它们分别模拟从服务器异步获取用户数据和文章数据。这两个函数都返回一个Promise对象,该对象在异步操作完成时解析。

然后,代码使用Promise.all方法来并行执行fetchUsersfetchPosts这两个异步任务。Promise.all接收一个Promise数组作为参数,并返回一个新的Promise对象。这个新的Promise对象会在所有传入的Promise都成功解析后解析,解析值为一个数组,包含所有传入Promise的解析值。

Promise.all.then回调中,代码处理了所有异步任务成功完成的情况。它打印出一条消息表示所有数据已加载完成,并分别打印出用户列表和文章列表。

如果任何一个异步任务失败,Promise.all返回的Promise对象会立即拒绝,并将拒绝原因传递给.catch回调。在.catch回调中,代码处理了数据加载失败的情况,并打印出错误信息。

总的来说,这段代码演示了如何使用Promise.all来并行处理多个异步任务,并在所有任务都完成后或任何一个任务失败时进行相应的处理。

3. 顺序执行多个异步任务

Promise可以链式调用,实现多个异步任务的顺序执行,确保每个任务在上一个任务完成后再执行:

// 定义一个模拟执行任务1的函数
const performTask1 = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('任务1完成'); // 打印任务1完成
            resolve('任务1结果'); // 将任务1的结果传递给resolve
        }, 1000); // 模拟异步执行任务1
    });
};

// 定义一个模拟执行任务2的函数
const performTask2 = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('任务2完成'); // 打印任务2完成
            resolve('任务2结果'); // 将任务2的结果传递给resolve
        }, 1500); // 模拟异步执行任务2
    });
};

// 执行任务1,然后在任务1完成后执行任务2
performTask1()
    .then(result => {
        console.log('任务1返回结果:', result); // 打印任务1返回的结果
        return performTask2(); // 返回执行任务2的Promise对象
    })
    .then(result => {
        console.log('任务2返回结果:', result); // 打印任务2返回的结果
        // 继续执行后续任务
    })
    .catch(error => {
        console.error('任务执行出错:', error); // 打印任务执行出错的错误信息
        // 处理错误情况
    });

这段代码定义了两个函数performTask1performTask2,它们分别模拟异步执行任务1和任务2。这两个函数都返回一个Promise对象,该对象在异步操作完成时解析,并传递任务的结果。

然后,代码通过调用performTask1函数开始执行任务1。在任务1的Promise解析后,.then回调被调用,并接收任务1的结果作为参数。在这个回调中,代码打印出任务1的结果,并返回performTask2函数的调用结果,这是一个新的Promise对象,代表任务2的执行。

当任务2的Promise解析后,第二个.then回调被调用,并接收任务2的结果作为参数。在这个回调中,代码打印出任务2的结果,并可以继续执行后续的任务。

如果任何一个任务失败,即任何一个Promise被拒绝,.catch回调会被调用,并接收拒绝原因作为参数。在这个回调中,代码可以处理错误情况,并打印出错误信息。

总的来说,这段代码演示了如何使用Promise的链式调用来顺序执行多个异步任务,并在每个任务完成后或任何一个任务失败时进行相应的处理。这种模式在实际开发中非常有用,特别是当您需要按顺序执行一系列异步操作,并且每个操作都依赖于前一个操作的结果时。

4. 处理并发的异步任务

Promise.race方法用于处理多个异步任务,只处理第一个完成的任务结果,忽略其余的任务:

// 定义一个模拟执行任务1的Promise对象
const task1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('任务1完成'), 1000); // 模拟异步执行任务1
});

// 定义一个模拟执行任务2的Promise对象
const task2 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('任务2完成'), 500); // 模拟异步执行任务2
});

// 使用Promise.race处理并发的异步任务
Promise.race([task1, task2])
    .then(result => {
        console.log('第一个完成的任务结果:', result); // 打印第一个完成的任务结果
        // 处理第一个完成的任务结果
    })
    .catch(error => {
        console.error('任务执行出错:', error); // 打印任务执行出错的错误信息
        // 处理错误情况
    });

这段代码定义了两个Promise对象task1task2,它们分别模拟异步执行任务1和任务2。task1将在1000毫秒后解析,而task2将在500毫秒后解析。

然后,代码使用Promise.race方法来处理这两个并发的异步任务。由于task2的解析时间比task1短,因此Promise.race返回的Promise对象将在task2解析时解析。

Promise.race.then回调中,代码处理了第一个完成的任务的结果。它打印出第一个完成的任务的结果,并可以在这里进行进一步的处理。

如果任何一个任务失败,即任何一个Promise被拒绝,Promise.race返回的Promise对象也会被拒绝,并将拒绝原因传递给.catch回调。在这个回调中,代码可以处理错误情况,并打印出错误信息。

总的来说,这段代码演示了如何使用Promise.race来处理多个并发的异步任务,并只关注第一个完成的任务的结果。这种模式在实际开发中非常有用,特别是当您有多个异步数据源,并且只需要从最快响应的那个数据源获取结果时。


七、总结

Promise作为JavaScript中处理异步操作的一种重要机制,极大地简化了代码编写和异步流程控制。在本篇文章中,我们深入探讨了Promise的核心概念、基本用法、进阶技巧以及如何手写一个简单的Promise,并通过实际应用示例展示了Promise在开发中的强大功能。

  1. Promise概述:Promise是JavaScript异步编程的核心,提供了一种更清晰、更直观的方式来处理异步操作。它具有三种状态:PendingFulfilledRejected,并且一旦状态改变,就不会再次改变。

  2. Promise的基本用法:通过Promise对象,我们可以使用then方法处理成功的异步结果,使用catch方法处理失败的异步结果,并使用finally方法在Promise结束时执行一些清理操作。

  3. Promise的进阶用法:Promise的链式调用和静态方法(如Promise.allPromise.race等)让我们能够更加灵活地处理多个异步任务,提供了并行和顺序执行异步任务的解决方案。

  4. 手写一个简单的Promise:通过手写一个简单的Promise实现,我们更加深入地理解了Promise的内部工作机制,包括状态管理、回调队列的处理以及异步任务的执行。

  5. Promise的实际应用:在实际开发中,Promise被广泛应用于异步数据加载、并行处理多个异步任务、顺序执行异步任务以及处理并发的异步任务等场景,提升了代码的可读性和维护性。

总的来说,掌握Promise对于现代JavaScript开发者来说至关重要。它不仅帮助我们解决了回调地狱问题,还让代码更加简洁和易于理解。希望通过这篇文章,你对Promise有了更加全面和深入的理解,并能够在实际开发中灵活运用它来编写高质量的异步代码。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/789270.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习(42)】通过vscode使用anaconda的python环境

按ctrlshiftp&#xff0c;选择Python:Select Interpreter 选择anaconda下的python虚拟环境

亚马逊关键词优化全攻略:自养号测评让你的产品跃居首页

常常听到亚马逊运营吐槽&#xff1a; 为啥我的产品就是上不了首页呢&#xff1f; 我的关键词要怎么优化才能排名靠前啊&#xff1f; 的确&#xff0c;每天都有无数个卖家在想方设法让自己的产品排到首页&#xff0c;所以产品的竞争激烈程度不言而喻。 我们在亚马逊运营中&a…

3Ds MAX 2025:创意的翅膀

初识3Ds MAX 2025 在我初次接触3Ds MAX 2025时&#xff0c;仿佛打开了一扇通往无限可能的大门。那时&#xff0c;我还是一个对三维建模充满好奇的初学者&#xff0c;心中怀揣着对未来的憧憬和对艺术的热爱。3Ds MAX 2025的出现&#xff0c;如同一位温柔的导师&#xff0c;带领…

C语言学习笔记[23]:循环语句while①

C语言除了顺序结构和选择结构还有循环结构 whilefordo...while while循环 //while 语法结构 while(表达式)循环语句; 表达式的结果为真&#xff0c;则执行循环语句&#xff0c;否则循环停止 例如&#xff1a;打印1~10 #include <stdio.h>int main() {int i 1;whil…

深度解析C++重载、隐藏、重写

重载 函数重载是指两个函数在同一个作用域并且函数名相同、参数(参数个数或类型或类型顺序 )不同的一种特殊情况 // 1、参数类型不同 int Add(int left, int right){cout << "int Add(int left, int right)" << endl;return left right; } double Add…

微软发布Win11 21H2七月更新补丁KB5040431,快来体验!

系统之家于7月10日发出最新报道&#xff0c;微软为Win11 21H2用户发布了七月的安全更新补丁KB5040431。用户升级系统后&#xff0c;会发现版本号升至22000.3079。此次更新针对远程桌面MultiPoint Server在争用条件会导致服务停止响应等多个问题进行修复。接下来跟随小编看看此次…

HarmonyOS ArkUi 字符串<展开/收起>功能

效果图&#xff1a; 官方API&#xff1a; ohos.measure (文本计算) 方式一 measure.measureTextSize 跟方式二使用一样&#xff0c;只是API调用不同&#xff0c;可仔细查看官网方式二 API 12 import { display, promptAction } from kit.ArkUI import { MeasureUtils } fr…

全面解析BPMN、CMMN、DMN与XML

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 全面解析BPMN、CMMN、DMN与XML 前言BPMN&#xff08;业务流程模型与标记法&#xff09;定义与用途…

基于GWO-CNN-BiLSTM数据回归预测(多输入单输出)-灰狼优化算法优化CNN-BiLSTM

基于GWO-CNN-BiLSTM数据回归预测(多输入单输出)-灰狼优化算法优化CNN-BiLSTM 1.数据均为Excel数据&#xff0c;直接替换数据就可以运行程序。 2.所有程序都经过验证&#xff0c;保证程序可以运行。 3.具有良好的编程习惯&#xff0c;程序均包含简要注释。 获取方式 https:/…

【Linux】进程7——查看进程

1.为什么进程管理这么重要呢&#xff1f; 这是因为&#xff1a; 首先&#xff0c;我们在操作系统时的各项任务其实都是经过某个PID来完成的&#xff08;包括你的bash环境&#xff09;&#xff0c;因此&#xff0c;能不能执行某项任务&#xff0c;就与该进程的权限有关了。再来…

图片管理不再愁,一文带你玩转图床世界

在数字化时代&#xff0c;图片已经成为我们日常生活中不可或缺的一部分。无论是社交媒体上的自拍分享&#xff0c;还是工作中的文档插图&#xff0c;图片都扮演着重要角色。 然而&#xff0c;你是否曾经遇到过这样的问题&#xff1a;如何在网络上方便地存储、分享和管理这些图…

【鸿蒙学习笔记】使用动画

官方文档&#xff1a;使用动画 目录标题 属性动画&#xff1a;通用属性发生改变时而产生的属性渐变效果animationanimateTo自定义属性动画 AnimatableExtend 转场动画&#xff1a;是页面或组件的切换动画 , 显示/隐藏 切换时的动画出现/消失转场&#xff1a;实现一个组件出现或…

详解[USACO07OPEN] Cheapest Palindrome G(洛谷PP2890)(区间DP经典题)

题目 思路 考虑区间DP。 设dp[i][j]为从i到j这段区间被修正为回文串的最小花费 c[cc][1]为添加字符cc的花费 c[cc][2]为删去字符cc的花费 s为题目给出的字符串。 用[i 1,j]区间转移&#xff1a;这种转移相当于在[i1,j]区间的左边加入一个字符&#xff0c;让[i,j]变为回文的方…

Linux镜像源设置不再难:一键脚本,新手也能成为优化高手(一键切换镜像源/Docker一键安装脚本)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 更换镜像源 📒📝 一键切换软件源📝 Docker一键安装脚本⚓️ 相关链接 ⚓️📖 介绍 📖 在国内,Linux系统用户经常会遇到下载软件包时速度慢的问题,这通常是因为默认的镜像源并不总是最优选择。对于新手来说,手动设置…

暑假学习计划怎么做 用待办计划软件安排更科学

暑期来临&#xff0c;无论是学生还是老师&#xff0c;做好暑期计划都至关重要。记得去年暑假&#xff0c;我给自己定下了阅读十本书的目标&#xff0c;却因为缺乏明确的计划&#xff0c;最后只草草读完了两本。而今年&#xff0c;我决定尝试一种新的方式——使用待办计划软件来…

每周算法:无向图的双连通分量

题目链接 冗余路径, Redundant Paths G 题目描述 为了从 F F F 个草场中的一个走到另一个&#xff0c;奶牛们有时不得不路过一些她们讨厌的可怕的树。 奶牛们已经厌倦了被迫走某一条路&#xff0c;所以她们想建一些新路&#xff0c;使每一对草场之间都会至少有两条相互分离…

VS安装Qt扩展工具

1-Visual Studio中安装QT插件 **插件下载地址&#xff1a;**http://download.qt.io/development_releases/vsaddin/ 关闭VS,双击下载的QT插件&#xff0c;默认安装即可&#xff1b; &#xff08;1&#xff09;配置Qt的MSVC编译器安装路径 打开Visual Studio&#xff0c;在菜单栏…

ceph存储

1 存储简介 存储的三种方式包括&#xff1a;块存储、文件存储、对象存储1。此外&#xff0c;还有内存存储、硬盘存储和闪存存储2。 内存存储&#xff1a;临时性数据存储方式&#xff0c;存储速度快&#xff0c;容量有限&#xff0c;通常用来存储正在使用的程序和数据。硬盘存…

AI绘画小白必备!Stable Diffusion常用插件合集,好用推荐!(附插件下载)

前言 宝子们&#xff0c;早上好啊~Stable Diffusion 常用插件&#xff0c;月月已经给大家整理好了&#xff0c;自取就好。 拥有这些SD常用插件&#xff0c;让您的图像生成和编辑过程更加强大、直观、多样化。以下插件集成了一系列增强功能&#xff0c;覆盖从自动补全提示词到…

windows10 +VS2019环境下的PCL安装和配置

今天想做点云重建&#xff0c;千篇一律&#xff0c;PCL少不了。一路跑下来觉得PCL的安装和环境配置还挺麻烦的&#xff0c;比OpenCV真的麻烦很多&#xff0c;有点不想写详细安装和配置过程了&#xff0c;偷个懒&#xff0c;就转载一下大佬的文章吧&#xff0c;下面的博主们已经…