Promise你真正了解清楚了吗?

Promise你真正了解清楚了吗?

编码文章call10242025-03-08 11:58:1236A+A-

在JavaScript中,Promise是一种解决异步编程问题的方式,一个Promise对象,代表了一个将要在本次操作完成后立即、稍后或从未实现的返回值。那么如何更优雅、高效的使用Promise呢?

并发控制

使用Promise.all 可以轻松管理多个Promise,使其并行执行,但是如果需要控制并发数量时,我们就需要实现一个控制函数。

const limitPromises = (promises, limit) => {
  return new Promise((resolve, reject) => {
    let result = [];
    for (let j = 0; j < promises.length j> {
        if (result.length >= Math.min(promises.length, limit)) {
          resolve(result);
        } else {
           result.push(value);
        }
       
      }).catch(reject);
    }
  });
}

超时控制

对Promise进行超时控制,指定一定时间无法返回的直接reject

const timeoutPromise = (promise, time) => {
  return Promise.race([promise, new Promise((r,j) => {
    setTimeout(() => {j(new Error('Timeout'))}, time);
  })])
}

取消控制

原生Promise是无法取消的,但是我们可以重新封装一个

class CancellablePromise {
  constructor(executor) {
    this.isCancelled = false;
    this.resolve = null;
    this.reject = null;

    const innerPromise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
      executor((...args) => {
        if (!this.isCancelled) {
          resolve(...args);
        }
      }, (...args) => {
        if (!this.isCancelled) {
          reject(...args);
        }
      });
    });

    Object.defineProperty(this, 'then', {
      value: innerPromise.then.bind(innerPromise)
    });

    Object.defineProperty(this, 'catch', {
      value: innerPromise.catch.bind(innerPromise)
    });
  }

  cancel() {
    this.isCancelled = true;
    if (this.resolve) {
      this.reject(new Error('Promise cancelled'));
      this.resolve = null;
      this.reject = null;
    }
  }
}

// 使用示例
const task = new CancellablePromise((resolve, reject) => {
  setTimeout(() => {
    resolve('Task completed');
  }, 5000);
});

task.then(result => {
  console.log(result);
}).catch(error => {
  console.error(error.message);
});

// 取消任务
setTimeout(() => {
  task.cancel();
}, 1000);

查询状态

原生Promise是无法查询状态的

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.promise = new Promise((resolve, reject) => {
      executor((...args) => {
        this.state = 'fulfilled';
        this.value = args.length === 1 ? args[0] : args;
        resolve(this.value);
      }, (...args) => {
        this.state = 'rejected';
        this.reason = args.length === 1 ? args[0] : args;
        reject(this.reason);
      });
    });
  }

  then(onFulfilled, onRejected) {
    return this.promise.then(onFulfilled, onRejected);
  }

  catch(onRejected) {
    return this.promise.catch(onRejected);
  }

  getState() {
    return this.state;
  }

  getValue() {
    if (this.state === 'fulfilled') {
      return this.value;
    }
    throw new Error('Promise is not fulfilled');
  }

  getReason() {
    if (this.state === 'rejected') {
      return this.reason;
    }
    throw new Error('Promise is not rejected');
  }
}

// 使用示例
const myPromise = new MyPromise((resolve, reject) => {
  setTimeout(() => reject('Success!'), 1000);
});

console.log(myPromise.getState()); // 'pending'

myPromise.then(result => {
  console.log(result); // 'Success!'
  console.log(myPromise.getState()); // 'fulfilled'
});

顺序控制

使Promise按顺序执行

const seqPromises = promises => {
  return promises.reduce((pre, next) => pre.then(next), Promise.resolve());
}
const asyncOperations = [
  () => new Promise((resolve) => setTimeout(() => resolve('Result 1'), 1000)),
  (result1) => new Promise((resolve) => setTimeout(() => resolve(`Result 2 based on ${result1}`), 1000)),
  (result2) => new Promise((resolve) => setTimeout(() => resolve(`Result 3 based on ${result2}`), 1000))
];
seqPromises(asyncOperations).then(res => console.log(res));

重试机制

在JavaScript中,处理Promise重试通常涉及创建一个能够自动重试失败操作的机制。这可以通过封装一个自定义的retry函数来实现,该函数接受一个返回Promise的函数作为参数,并根据指定的重试次数和延迟时间来决定是否重新执行该Promise。

以下是一个简单的retry函数实现,它接受三个参数:一个返回Promise的异步函数fn,最大重试次数maxRetries,以及每次重试之间的延迟时间delay(以毫秒为单位)。

function retry(fn, maxRetries = 3, delay = 1000) {
  return new Promise((resolve, reject) => {
    let attempt = 0;

    const attemptFn = () => {
      if (attempt >= maxRetries) {
        reject(new Error(`Max retries reached: ${maxRetries}`));
        return;
      }

      attempt++;

      fn()
        .then(resolve)
        .catch((error) => {
          setTimeout(() => {
            console.warn(`Attempt ${attempt} failed, retrying...`, error);
            attemptFn();
          }, delay);
        });
    };

    attemptFn();
  });
}

// 使用示例
const asyncOperation = () => {
  return new Promise((resolve, reject) => {
    // 模拟一个可能会失败的异步操作
    const success = Math.random() > 0.5; // 50% 的成功率
    if (success) {
      resolve('Operation succeeded');
    } else {
      reject(new Error('Operation failed'));
    }
  });
};

retry(asyncOperation, 5, 2000)
  .then((result) => {
    console.log('Final result:', result);
  })
  .catch((error) => {
    console.error('Operation failed after retries:', error);
  });

确保Promise只解决一次

在JavaScript中,Promise 对象的设计初衷就是确保它们只解决(resolve)一次或拒绝(reject)一次。一旦 Promise 进入已解决(fulfilled)或已拒绝(rejected)状态,它的状态就是最终的,不可再改变。这意味着你不能“重新解决”或“重新拒绝”一个已经解决或拒绝的 Promise。

然而,有时候你可能会遇到需要确保某个异步操作只被触发一次的情况,即使这个操作被封装在一个可能多次调用的函数或回调中。这通常不是 Promise 本身的问题,而是你如何使用和管理 Promise 的问题。

function once(fn) {
  let resolved = false;
  let result;

  return function(...args) {
    if (!resolved) {
      resolved = true;
      result = fn.apply(this, args);

      if (result && typeof result.then === 'function') {
        // 如果 fn 返回一个 Promise,则确保它只被处理一次
        result.finally(() => {
          // 可以在这里做一些清理工作,但通常不需要
        });
      }
    }

    return result;
  };
}

// 使用示例
const asyncOperation = () => new Promise(resolve => setTimeout(resolve, 1000, 'done'));
const asyncOnce = once(asyncOperation);

asyncOnce().then(console.log); // 输出 'done'
asyncOnce().then(console.log); // 不会执行,因为 Promise 已经被解决

Promise.allSettled

使用 Promise.allSettled() 的一个常见场景是当你需要并行执行多个异步操作,并且不关心其中任何一个操作是否失败,只想在所有操作完成后获取它们的结果(无论是成功还是失败)。

const promise1 = Promise.resolve(42);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'error'));
const promise3 = Promise.resolve('Hello World');

Promise.allSettled([promise1, promise2, promise3])
  .then((results) => {
    results.forEach((result) => {
      if (result.status === 'fulfilled') {
        console.log(`Fulfilled with value: ${result.value}`);
      } else if (result.status === 'rejected') {
        console.log(`Rejected with reason: ${result.reason}`);
      }
    });
  });

以上是对于Promise一些特殊用法的总结,你学废了吗?

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4