Promise简单理解

286 阅读7分钟

Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点。

  • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。

Promise的基本使用

一、传统用法

 const p1 = new Promise((resolve, reject) => {
        const image = new Image();
        image.onload = () => {
            resolve(image);
        };
        image.onerror = () => {
            reject(new Error('Could not load image'));
        };
        image.src = 'https://gips3.baidu.com/it/u=3732737575,1337431568&fm=3028&app=3028&f=JPEG&fmt=auto&q=100&size=f1440_2560';
     });
     // then接收两个参数 一个onFulfilled(成功之后的回调) 另一个是onReject(失败时候的回调)
    p1.then(
        (res)=>{console.log(res)},
        (err)=>{console.log(err)}
    );
    //失败时候的回调 也可以catch进行接收
  p1
    .then((res)=>{console.log(res)})
    .catch((err)=>{console.log(err)})
    .finally(()=>{console.log('无论成功还是失败finally函数都会执行')});

二 、结合 async && await 使用

   const p1 = new Promise((resolve, reject) => {
          const image = new Image();
          image.onload = () => {
              resolve(image);
          };
          image.onerror = () => {
              reject(new Error('Could not load image'));
          };
          image.src = 'https://gips3.baidu.com/it/u=3732737575,1337431568&fm=3028&app=3028&f=JPEG&fmt=auto&q=100&size=f1440_2560';
       });
   async function test() {
      const image = await p1;
      console.log(image);
   }
  test();

Promise的四大方法

1、 Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,当所有都返回状态都变成fulfilled,则执行Promise.all成功之后的回调,若其中有一个变为rejected,会调用Promise.all方法失败后的回调函数。

    // 异步加载多张图片,等所有图片全部加载完毕在进行渲染
   const images = [
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
    ]
    const newImages = images.map(img=>{
        return new Promise((resolve, reject) => {
            const image = new Image();
            image.src = img;
            image.onload = function() {
                resolve(image);
            }
            image.onerror = function() {
                reject(new Error('Could not load image at ' + img));
            }
        });
    });
    Promise.all(newImages).then((images) => {
        images.forEach((image) => {
            document.body.appendChild(image);
        });
    }).catch((error) => {
        console.log(error);
    });

2、 Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。race有赛跑的意思,看多个Promise实例中谁跑的最快(即谁的状态先发生改变,从pending->fulfilled 或pending->rejected)这个时候不看它是成功还是失败,只要跑的快。

  const images = [
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo2.png',
        'https://www.baidu.com/img/bd_logo3.png',
    ]
    const newImages = images.map((img,index)=>{
        return new Promise((resolve, reject) => {
           if(index ==1) setTimeout(() => resolve(img), 1000);
           if(index ==2) setTimeout(() => resolve(img), 2000);
           if(index ==0) setTimeout(() => resolve(img), 3000);
        });
    });
    Promise.race(newImages).then((images) => {
        console.log(images); // https://www.baidu.com/img/bd_logo2.png  1s后输出
    }).catch((error) => {
        console.log(error);
    });

3、 Promse.allSettled

有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。> 但是,现有的 Promise 方法很难实现这个要求。 Promise.all()方法只适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求。

const urls = [url_1, url_2, url_3];
const requests = urls.map(x => fetch(x));

try {
  await Promise.all(requests);
  console.log('所有请求都成功。');
} catch {
  console.log('至少一个请求失败,其他请求可能还没结束。');
}

Promise.all()可以确定所有请求都成功了,但是只要有一个请求失败,它就会报错,而不管另外的请求是否结束。

Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

 const newImages = images.map(img=>{
        return new Promise((resolve, reject) => {
            const image = new Image();
            image.src = img;
            image.onload = function() {
                resolve(image);
            }
            image.onerror = function() {
                reject(new Error('Could not load image at ' + img));
            }
        });
    });
    Promise.allSettled(newImages).then((images) => {
        console.log(images);
        /** 
        images对象是一个数组,每一项包含对应的状态,以及对应的返回值,
         可以根据状态过滤出成功项有哪些。
        [
            {
                "status": "fulfilled",
                "value": {}
            },
            {
                "status": "fulfilled",
                "value": {}
            },
            {
                "status": "fulfilled",
                "value": {}
            },
            {
                "status": "rejected",
                "reason": {}
            },
            {
                "status": "fulfilled",
                "value": {}
            }
        ]
        **/
    }).catch((error) => {
        console.log(error);
    });

4、Promise.any()

Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数有一个变成fulfilled状态,包装实例就会变成fullfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。(有点像数组中some方法)

 const images = [
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
        'https://www.baidu.com/img/bd_logo1.png',
    ]
    const newImages = images.map(img=>{
        return new Promise((resolve, reject) => {
            const image = new Image();
            image.src = img;
            image.onload = function() {
                resolve(image);
            }
            image.onerror = function() {
                reject(new Error('Could not load image at ' + img));
            }
        });
    });
    Promise.any(newImages).then((images) => {
        console.log(images);
    }).catch((error) => {
        console.log(error);
    });

Promise源码

写一个简易的Promise,有问题还请各位大佬批评指正

class MyPromise {
    constructor(executor) {
        this.state = 'pending';  // 初始状态
        this.value = undefined;  // 存储结果或错误
        this.onFulfilledCallbacks = [];  // 成功回调队列
        this.onRejectedCallbacks = [];   // 失败回调队列

        const resolve = (value) => {
            if (this.state !== 'pending') return;
            this.state = 'fulfilled';
            this.value = value;
            this.onFulfilledCallbacks.forEach(callback => callback(value));
        };

        const reject = (reason) => {
            if (this.state !== 'pending') return;
            this.state = 'rejected';
            this.value = reason;
            this.onRejectedCallbacks.forEach(callback => callback(reason));
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            const handleFulfilled = (value) => {
                try {
                    if (typeof onFulfilled === 'function') {
                        const result = onFulfilled(value);
                        resolve(result);
                    } else {
                        resolve(value);
                    }
                } catch (error) {
                    reject(error);
                }
            };

            const handleRejected = (reason) => {
                try {
                    if (typeof onRejected === 'function') {
                        const result = onRejected(reason);
                        resolve(result);
                    } else {
                        reject(reason);
                    }
                } catch (error) {
                    reject(error);
                }
            };

            if (this.state === 'fulfilled') {
                setTimeout(() => handleFulfilled(this.value), 0);
            } else if (this.state === 'rejected') {
                setTimeout(() => handleRejected(this.value), 0);
            } else {
                this.onFulfilledCallbacks.push(value => handleFulfilled(value));
                this.onRejectedCallbacks.push(reason => handleRejected(reason));
            }
        });
    }

    // 静态方法:所有 Promise 都成功时返回结果数组
    static all(promises) {
        return new MyPromise((resolve, reject) => {
            const results = [];
            let completed = 0;
            const total = promises.length;

            if (total === 0) {
                resolve([]);
                return;
            }

            promises.forEach((promise, index) => {
                MyPromise.resolve(promise)
                    .then(value => {
                        results[index] = value;
                        completed++;
                        if (completed === total) {
                            resolve(results);
                        }
                    })
                    .catch(error => reject(error));
            });
        });
    }

    // 静态方法:返回第一个成功的 Promise 的值
    static any(promises) {
        return new MyPromise((resolve, reject) => {
            let rejectedCount = 0;
            const errors = [];

            if (promises.length === 0) {
                reject(new Error('All promises were rejected'));
                return;
            }

            promises.forEach((promise, index) => {
                MyPromise.resolve(promise)
                    .then(value => resolve(value))
                    .catch(error => {
                        errors[index] = error;
                        rejectedCount++;
                        if (rejectedCount === promises.length) {
                            reject(errors);
                        }
                    });
            });
        });
    }

    // 静态方法:等待所有 Promise 完成,无论成功或失败
    static allSettled(promises) {
        return new MyPromise((resolve) => {
            const results = [];
            let completed = 0;
            const total = promises.length;

            if (total === 0) {
                resolve([]);
                return;
            }

            promises.forEach((promise, index) => {
                MyPromise.resolve(promise)
                    .then(value => {
                        results[index] = { status: 'fulfilled', value };
                    })
                    .catch(reason => {
                        results[index] = { status: 'rejected', reason };
                    })
                    .then(() => {
                        completed++;
                        if (completed === total) {
                            resolve(results);
                        }
                    });
            });
        });
    }

    // 静态方法:返回第一个完成的 Promise 的结果(无论成功或失败)
    static race(promises) {
        return new MyPromise((resolve, reject) => {
            if (promises.length === 0) {
                return;
            }

            promises.forEach(promise => {
                MyPromise.resolve(promise)
                    .then(value => resolve(value))
                    .catch(error => reject(error));
            });
        });
    }

    // 辅助方法:将值包装成 Promise
    static resolve(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value));
    }

    // 辅助方法:创建一个 rejected 状态的 Promise
    static reject(reason) {
        return new MyPromise((_, reject) => reject(reason));
    }
}

// 测试代码
const p1 = new MyPromise(resolve => setTimeout(() => resolve('p1'), 1000));
const p2 = new MyPromise(resolve => setTimeout(() => resolve('p2'), 500));
const p3 = new MyPromise((_, reject) => setTimeout(() => reject('p3 error'), 800));

// 测试 Promise.all
MyPromise.all([p1, p2])
    .then(results => console.log('all:', results))
    .catch(error => console.log('all error:', error));

// 测试 Promise.any
MyPromise.any([p3, p1])
    .then(result => console.log('any:', result))
    .catch(errors => console.log('any errors:', errors));

// 测试 Promise.allSettled
MyPromise.allSettled([p1, p3])
    .then(results => console.log('allSettled:', results));

// 测试 Promise.race
MyPromise.race([p2, p3])
    .then(result => console.log('race:', result))
    .catch(error => console.log('race error:', error));