Promise、async/await、RxJs

70 阅读6分钟

Promise

promise状态:

Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)

promise基本使用:

 new Promise((resolve,reject)=>{...})
.then(success=>{...})
.catch(error=>{...})
.finally(()=>{...});

Promise.resolve();
Promise.reject();

Promise.all();
Promise.race();
  

promise需要注意的地方:

1.then(success=>{},error=>{})中的error回调可以捕获reject结果,但是不能捕获success回调函数中的异常,所以我们推荐使用catch()函数捕获reject结果

2.then().then()的调用链中return 新的promise对象会导致执行顺序的改变,如:

const p1 = new Promise((resolve, reject) => {
  console.log('promise1');//1
  resolve();
})
  .then(() => {
    console.log('then11');//3
    new Promise((resolve, reject) => {
      console.log('promise2');//4
      resolve();
    })
      .then(() => {
        console.log('then21');//6
      })
      .then(() => {
        console.log('then23');//8
      });
  })
  .then(() => {
    console.log('then12');//7
  });

const p2 = new Promise((resolve, reject) => {
  console.log('promise3');//2
  resolve();
}).then(() => {
  console.log('then31');//5
});
const p1 = new Promise((resolve, reject) => {
  console.log('promise1'); // 1
  resolve();
})
  .then(() => {
    console.log('then11'); // 2
    return new Promise((resolve, reject) => {
      console.log('promise2'); // 3
      resolve();
    })
      .then(() => {
        console.log('then21'); // 4
      })
      .then(() => {
        console.log('then23'); // 5
      });
  })
  .then(() => {
    console.log('then12'); //6
  });

3.promise.then()期望参数是一个函数,如果不是函数就会发生值透传。就算发生了透传,promise.then()中的代码依旧也是会执行的。

基本使用

  1. Promise.all([])

当数组中的所有 Promise 实例都成功时,它会按照请求的顺序返回成功结果数组。如果任何 Promise 失败,它就会进入失败回调。

const p1 = new Promise((resolve) => {resolve(1);});

const p2 = new Promise((resolve) => {resolve(1);});

const p3 = Promise.resolve('ok');

// If all promises succeed, result will be an array of 3 results.const result = Promise.all([p1, p2, p3]);
// If one fails, the result is the failed promise's value
  1. Promise.allSettled([])

执行不会失败,它返回一个与输入数组中每个 Promise 实例的状态相对应的数组。

const p1 = Promise.resolve(1);

const p2 = Promise.reject(-1);

Promise.allSettled([p1, p2]).then(res => {
    console.log(res);
});
// Output:/*  [  { status: 'fulfilled', value: 1 },  { status: 'rejected', reason: -1 }  ]*/
  1. Promise.any([])

如果输入数组中的任何 Promise 状态变为 fulfilled,则返回的实例将变为 fulfilled 状态并返回第一个 fulfilled 的 Promise 的值。如果全部被拒绝,那么最终的状态就为拒绝。

const p1 = new Promise((resolve, reject) => {
    reject(1);
});

const p2 = new Promise((resolve, reject) => {   reject(2);});

const p3 = Promise.resolve("ok");

Promise.any([p1, p2, p3]).then((r) => console.log(r));
  1. Promise.race([])

一旦数组中任何一个Promise的状态发生改变,race方法的状态就会随之改变;第一个改变的Promise的值将被传递给race方法的回调。

const p1 = new Promise((resolve) => {
    setTimeout(() => {
        resolve(10);
    }, 3000);});
    
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
       throw new Error("I encountered an error");
    }, 2000);
});

Promise.race([p1, p2]).then((v) => console.log(v));

抛出异常并不会改变 race 状态;最终状态仍然由 p1 决定。

高阶技巧

以下 9 个高阶技巧,可帮助开发人员更高效、更优雅地处理异步操作。

  1. 并发控制

使用 Promise.all 可以并行执行多个 Promises,另外可以实现并发控制函数来控制同时请求的数量。

const concurrentPromises = (promises, limit) => {   
    return new Promise((resolve, reject) => {
        let i0;
        let result = [];
        const executor = () => {
            if (i >= promises.length) {
                return resolve(result);
            }
            const promise = promises[i++];
            Promise.resolve(promise)
            .then(value => {
                result.push(value);
                if (i < promises.length) {
                    executor();
                } else {
                    resolve(result);
                }
            })
            .catch(reject);
        };
        for (let j0; j < limit && j < promises.length; j++) {           
            executor();
        }
   });
};
  1. Promise Timeout

有时,我们希望如果 Promise 在一定时间范围内一直未决,则自动拒绝。可以按如下方式实现。

const promiseWithTimeout = (promise, ms) =>
    Promise.race([promise,
        new Promise((resolve, reject) => setTimeout(() => reject(new Error('Timeout after ' + ms + 'ms')), ms))
    ]);
  1. 可取消的Promise

原生 JavaScript Promises 无法取消,但你可以通过引入可控的中断逻辑来模拟取消。

const cancellablePromise = promise => {
    let isCanceledfalse;
    const wrappedPromise = new Promise((resolve, reject) => {
        promise.then(
            value => (isCanceled ? 
                        reject({ isCanceled, value }) 
                        : resolve(value)),
                        error => (isCanceled ? reject({ isCanceled, error }) 
                        : reject(error))
                      );
        });
        return {
            promise: wrappedPromise,
            cancel() {
                isCanceledtrue;
            }
        };
};
  1. 按顺序执行 Promise 数组

有时我们需要按顺序执行一系列 Promises,以确保前一个异步操作完成后再开始下一个操作。

const sequencePromises = promises =>
    promises.reduce(
        (prev, next) => prev.then(() => next()),       
        Promise.resolve()
    );
  1. Promises 重试

当 Promise 由于临时错误而被拒绝时,我们可能想要重试其执行。

const retryPromise = (promiseFn, maxAttempts, interval) => {   return new Promise((resolve, reject) => {
    const attempt = attemptNumber => {
        if (attemptNumber === maxAttempts) {
            reject(new Error('Max attempts reached'));
            return;
        }
        promiseFn().then(resolve).catch(() => {
            setTimeout(() => {
                attempt(attemptNumber + 1);
            }, interval);
        });
    };
    attempt(0);
 });
};
  1. 确保 Promise 仅 Resolve 一次

在某些情况下,可能希望确保 Promise 只 resolve 一次,即使多次调用 resolve。

const onceResolvedPromise = executor => {
    let isResolvedfalse;
    return new Promise((resolve, reject) => {
        executor(
            value => {
                if (!isResolved) {
                    isResolvedtrue;
                    resolve(value);
                }
            },
            reject
            );
     });
};
  1. 使用 Promises 代替回调

Promise 已经完全替代回调函数,成为一种更标准化、更便捷的处理异步操作方式。

const callbackToPromise = (fn, ...args) => {
    return new Promise((resolve, reject) => {
        fn(...args, (error, result) => {
            if (error) {
                reject(error);
            } else {
                resolve(result);
            }
        });
    });
};
  1. 动态生成 Promise 链

在某些情况下,可能需要根据不同的条件动态创建一系列 Promise 链。

const tasks = [task1, task2, task3]; // Array of asynchronous tasks

const promiseChain = tasks.reduce((chain, currentTask) => {   
    return chain.then(currentTask);
}, Promise.resolve());
  1. 使用 Promises 实现简单的异步锁

在多线程环境中,可以使用 Promises 实现简单的异步锁,从而确保一次只有一个任务可以访问共享资源。

let lock = Promise.resolve();

const acquireLock = () => {
    let release;
    const waitLock = new Promise(resolve => {
        release = resolve;
    });
    const tryAcquireLock = lock.then(() => release);
    lock = waitLock;
    return tryAcquireLock;
};

此代码不断创建和解析 Promises,实现一个简单的 FIFO 队列,以确保只有一个任务可以访问共享资源。lock 变量代表当前是否有任务正在执行,始终指向正在进行的任务的 Promise。acquireLock 函数用于请求执行权限并创建一个新的 Promise 来等待当前任务完成。

参考:

从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节

Promise.any 的作用,如何自己实现一个 Promise.any

Promise.allSettled 的作用,如何自己实现一个 Promise.allSettled

如何来控制promise执行的并发数

这一次,彻底弄懂 Promise 原理

面试官:“你能手写一个 Promise 吗”

一起拿下异步

你真的完全掌握了promise么?

【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)

进来看看ES6 Promise最全手写实现

Promise实现原理(附源码)

面试精选之Promise

Promise不会??看这里!!!史上最通俗易懂的Promise!!!

前端工程师必知之Promise的实现

彻底理解Promise原理及全功能实现

Promise 被玩出 48 种“花样”,深度解析10个常用模块

分享 9个 使用promises的技巧


【译】async/await 应知应会

[译]JavaScript: Promises 介绍及为何 Async/Await 最终取得胜利

[译文]如何避开 async/await 地狱

一次性让你懂async/await,解决回调地狱

手写async、await、generator核心逻辑

嘿,不要给 async 函数写那么多 try/catch 了

说一说如何解决async await处理异常问题

面试官: 说说你对async的理解

async原理解析

7张图,20分钟就能搞定的async/await原理!

面试官:你给我讲讲async/await


作为前端,你需要知道 RxJS(响应式编程-流)

RxJS教程

RxJS 源码解析(五)—— Operator III

RxJS——给你如丝一般顺滑的编程体验(篇幅较长,建议收藏)