• 异步操作中Promise对象的用法总结
  • 发布于 6小时前
  • 6 热度
    0 评论
介绍
Promise 对象表示异步操作的最终完成(或失败)及其结果值。
Promise 有下面三种状态:
1.Pending:初始状态,既非 fulfilled 也非 rejected。
2.Fulfilled:操作成功完成。
3.Rejected:操作失败。
不同于之前的回调风格,使用 Promises 有如下的特点:
1.在当前事件循环完成之前,不会调用回调函数。
2.异步操作完成(成功或失败),通过 then() 添加的回调将被调用。
3.可以通过多次调用 then() 添加多个回调,它们将按照添加的顺序执行。
Promises 的典型特征就是链式调用。

基本使用
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

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 }
  ]
*/
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), // Outputs 'ok'
  (e) => console.log(e)
);
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), // Outputs 10
  (e) => console.log(e)
);
抛出异常并不会改变 race 状态;最终状态仍然由 p1 决定。

高阶技巧
以下 9 个高阶技巧,可帮助开发人员更高效、更优雅地处理异步操作。
并发控制
使用 Promise.all 可以并行执行多个 Promises,另外可以实现并发控制函数来控制同时请求的数量。
const concurrentPromises = (promises, limit) => {
   return new Promise((resolve, reject) => {
       let i = 0;
       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 j = 0; j < limit && j < promises.length; j++) {
           executor();
      }
  });
};
Promise Timeout
有时,我们希望如果 Promise 在一定时间范围内一直未决,则自动拒绝。可以按如下方式实现。
const promiseWithTimeout = (promise, ms) =>
   Promise.race([
       promise,
       new Promise((resolve, reject) =>
           setTimeout(() => reject(new Error('Timeout after ' + ms + 'ms')), ms)
      )
  ]);
可取消的Promise
原生 JavaScript Promises 无法取消,但你可以通过引入可控的中断逻辑来模拟取消。
const cancellablePromise = promise => {
   let isCanceled = false;
   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() {
           isCanceled = true;
      }
  };
};
按顺序执行 Promise 数组
有时我们需要按顺序执行一系列 Promises,以确保前一个异步操作完成后再开始下一个操作。
const sequencePromises = promises =>
   promises.reduce(
      (prev, next) => prev.then(() => next()),
       Promise.resolve()
  );
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);
  });
};
确保 Promise 仅 Resolve 一次
在某些情况下,可能希望确保 Promise 只 resolve 一次,即使多次调用 resolve。
const onceResolvedPromise = executor => {
   let isResolved = false;
   return new Promise((resolve, reject) => {
       executor(
           value => {
               if (!isResolved) {
                   isResolved = true;
                   resolve(value);
              }
          },
           reject
      );
  });
};
使用 Promises 代替回调
Promise 已经完全替代回调函数,成为一种更标准化、更便捷的处理异步操作方式。
const callbackToPromise = (fn, ...args) => {
   return new Promise((resolve, reject) => {
       fn(...args, (error, result) => {
           if (error) {
               reject(error);
          } else {
               resolve(result);
          }
      });
  });
};
动态生成 Promise 链
在某些情况下,可能需要根据不同的条件动态创建一系列 Promise 链。
const tasks = [task1, task2, task3]; // Array of asynchronous tasks

const promiseChain = tasks.reduce((chain, currentTask) => {
   return chain.then(currentTask);
}, Promise.resolve());
使用 Promises 实现简单的异步锁
在多线程环境中,可以使用 Promises 实现简单的异步锁,从而确保一次只有一个任务可以访问共享资源。
let lock = Promise.resolve();
// 堆代码 duidaima.com
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 来等待当前任务完成。

结论
Promises 是现代 JavaScript 异步编程中不可或缺的一部分。掌握 Promises 将大大提高开发效率和代码质量。通过上面根据场景所给出的的各种示例代码,开发人员可以更自信地处理复杂的异步场景,并编写更易读、更优雅、更强大的代码。
用户评论