这都是妥妥的项目经验,Promise的用法实践

291 阅读2分钟

前言

在JavaScript中,Promise是一种解决异步编程问题的重要方式,下面从累积项目经验中提取Promise 的实战用法,相互学习,相互交流~

1. Promise 控制并发请求,分批处理接口

实战场景:假设有100个请求,每次控制发送5个请求,其中一个请求完毕,再加入新的请求,直到全部请求完毕。这时如果使用promsie.all,浏览器会同时发送100个请求,这样可能会造成请求阻塞、页面卡顿、甚至服务器崩溃。

// 自定义模拟接口函数
const request = params => {
  return new Promise(resolve => {
      setTimeout(() => {
          resolve(`执行任务编号:${params}`)
      }, 1000)
  }).then(res => {
      console.log(res);
  })
}

// 执行任务
async function promiseTask(max = 5){
  const urls = Array.from({length: 100}).map((item, index) => `url${index + 1}`)
  const pool = [] // 并发池
  for(let i=0; i<urls.length; i++){
    let url = urls[i] 
    let task = request(url); // 当前执行的任务
    task.then((data)=>{
        //每当并发池跑完一个任务,从并发池删除个任务
        pool.splice(pool.indexOf(task), 1)
        console.log(`${url} 结束`);
    })
    pool.push(task); 
    if(pool.length === max){
        // 利用Promise.race方法来获得并发池中某任务完成的信号,
        // 和await结合当有任务完成才让程序继续执行,让循环把并发池塞满
        await Promise.race(pool)
    }
  } 
}

promiseTask(5)

2. Promise 处理接口超时

实战场景:希望 Promise 在一定时间内没有的到解决就自动停止。 我们知道Promise.race方法接收多个Promise,其中任意一个Promise存在 resove 或者 reject,其他的就不会执行了。基于这个特点,我们可以构造代码实现给定超时时间的接口超时终止处理

// 自定义模拟请求函数的请求时间为 1000 毫秒
function request() {
  return new Promise(resolve => {
      setTimeout(() => {
          resolve(`接口请求成功`)
      }, 1000)
  }).then(res => {
      console.log(res);
  })
}

// 通过Promise.race 实现,请求的接口和指定时间的promise 作为 Promise.race 的参数进行赛跑
const promiseTask = (request, time) => {
  Promise.race([
    request(),
    new Promise((resolve,reject) => {
      setTimeout(() => {
        reject(new Error(`超时${time}毫秒`))
      }, time)
    }).then(res => {
      console.log(res)
    })
  ])
}

promiseTask(request, 500);

3. Promise 取消

实战场景:原生的Promise构造不支持取消操作,在如下例子中cancellablePromise函数创建了一个可以取消的Promise。通过调用promise.cancel()方法,可以设置一个标志来拒绝Promise,从而取消它。如果尝试在取消后使用thencatch方法,将不会有任何效果,因为Promise已经被处理了

function cancellablePromise(executor) {
  let isCancelled = false;
  const promiseRes = new Promise((resolve, reject) => {
    executor(resolve, () => {
      isCancelled = true;
      reject(new Error('Promise cancelled'));
    });
  });
  // 定义一个cancel方法,用来控制
  promiseRes.cancel = function() {
    isCancelled = true;
  };
  return promiseRes;
}
 
const cancellable = cancellablePromise((resolve, reject) => {
  setTimeout(() => {
    if (!cancellable.isCancelled) {
      resolve('Operation completed');
    }
  }, 1000);
});
 
cancellable.cancel(); // 取消Promise
cancellable.then(console.log).catch(console.error);

4. Promise 的顺序执行

实战场景: 有时候我们需要按顺序执行一组Promise,以确保前一个异步操作完成后再开始下一个。

const promiseTask = (promiseArr) => {
  promiseArr.reduce(
   (prev, next) => prev.then(() => next()),
   Promise.resolve()
  )
}

5. Promise 的重试

实战场景:当Promise因为某些暂时性的错误被拒绝时,可能希望能够重试执行。下面是通过递归方法 attempt(attemptNumber) 实现重试

const promiseTask = (promiseFn, maxTimes) => {
  return new Promise((resolve, reject) => {
    const attempt = attemptNumber => {
      if (attemptNumber === maxTimes) {
        reject(new Error('达到最大重试次数'));
        return;
      }
      promiseFn().then(resolve).catch(() => {
        setTimeout(() => {
          attempt(attemptNumber + 1);
        }, 1000);
      });
    };
    attempt(0);
  });
};

6. Promise 处理多个异步操作

Promise.allPromise.allSettled都会等待处理所有的异步结果,但是不同的是,任何其中一个Promise 拒绝,Promise.all() 会立即拒绝;而 Promise.allSettled会等待所有的 Promise 解决或者拒绝再返回结果。

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

Promise.all([promise1, promise2])
 .then((values) => {
	console.log(values);
})
 .catch((error) => {
 	console.log('An error occurred in Promise.all():', error);
});

// Output:
// An error occurred in Promise.all(): Promise 2 失败
const promise1 = Promise.resolve('Promise 1 成功');
const promise2 = Promise.reject('Promise 2 失败');

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

// Output:
// Promise 1 was fulfilled with value: Promise 1 成功
// Promise 2 was rejected with reason: Promise 2 失败

7. Promise 实现一个sleep 函数

可以通过 Promise 实现一个阻塞函数

async sleep(time) {
  return new Promise((resolve, reject) => {
     let timerId = setTimeout(() => {
        clearTimeout(timerId);
        resolve()
     }, time)
  })
}