翼度科技»论坛 编程开发 JavaScript 查看内容

[JS] promise知识点与应用场景

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
Promise是JS中用于处理异步操作的方法,- 支持链式调用从而解决了地狱回调问题。
Promise的基础用法

状态

promise有三种状态:

  • Pending(待定):初始状态,既不是成功也不是失败。
  • Fulfilled(已成功):操作成功完成。
  • Rejected(已失败):操作失败。
  1. const promise = new Promise((resolve, reject) => {
  2.   // 异步操作
  3.   if (成功) {
  4.     resolve(value);
  5.   } else {
  6.     reject(error);
  7.   }
  8. });
复制代码
实例方法

Promise有三个实例方法,分别是then,catch,和finally。

  • then用于处理Promise成功的情况:
  1. promise.then((value) => {
  2.   console.log(value);
  3. });
复制代码

  • catch用于处理Promise失败的情况,即异常捕获:
  1. promise.catch((error) => {
  2.   console.error(error);
  3. });
复制代码

  • finally:无论Promise最终状态如何(成功或失败),都会执行finally中的回调。
  1. promise.finally(() => {
  2.   console.log('操作完成');
  3. });
复制代码
链式调用

then方法可以返回一个Promise,并在后续链式地继续调用then方法。
  1. doSomething()
  2.   .then((result) => {
  3.     return doSomethingElse(result);
  4.   })
  5.   .then((newResult) => {
  6.     return doThirdThing(newResult);
  7.   })
  8.   .then((finalResult) => {
  9.     console.log(`Final result: ${finalResult}`);
  10.   })
  11.   .catch((error) => {
  12.     console.error(error);
  13.   });
复制代码
链式调用只需要在尾部调用一次catch,在链式调用的过程中发生的异常都会被这个尾部的catch捕获。
静态方法


  • Promise.resolve(value):返回一个成功的Promise,值为value;常见于后面跟上then方法将一个函数推入微任务队列;
  • Promise.reject(reason):返回一个失败的Promise,原因为reason;
  • Promise.all(iterable):并行执行多个Promise,所有Promise都成功时返回一个包含所有结果的新Promise,如果有任何一个失败,则返回失败的Promise。
  1. Promise.all([promise1, promise2, promise3])
  2.   .then((values) => console.log(values))
  3.   .catch((error) => console.error(error));
复制代码

  • Promise.race(iterable):返回第一个完成的Promise,无论成功还是失败。
  1. Promise.race([promise1, promise2, promise3])
  2.   .then((value) => console.log(value))
  3.   .catch((error) => console.error(error));
复制代码
Promise.all的应用场景

并发请求,有时候在一个页面中需要使用多个GET请求获取页面数据并渲染,并且这些GET请求没有依赖关系,即不需要考虑请求顺序。那么这时就可以使用Promise.all并发执行这些GET请求。
  1. const fetchUser = fetch('https://api.example.com/user');
  2. const fetchPosts = fetch('https://api.example.com/posts');
  3. const fetchComments = fetch('https://api.example.com/comments');
  4. Promise.all([fetchUser, fetchPosts, fetchComments])
  5.   .then(([userResponse, postsResponse, commentsResponse]) => {
  6.     return Promise.all([userResponse.json(), postsResponse.json(), commentsResponse.json()]);
  7.   })
  8.   .then(([userData, postsData, commentsData]) => {
  9.     console.log(userData, postsData, commentsData);
  10.   })
  11.   .catch((error) => {
  12.     console.error('请求失败', error);
  13.   });
复制代码
并发执行需要注意并发量不要太大,我们可以通过实现一个并发控制的类来限制并发量。
  1. class RequestScheduler {
  2.   constructor(concurrencyLimit) {
  3.     this.concurrencyLimit = concurrencyLimit;
  4.     this.running = 0;
  5.     this.queue = [];
  6.   }
  7.   // 添加请求到队列
  8.   add(requestFn) {
  9.     return new Promise((resolve, reject) => {
  10.       this.queue.push({ requestFn, resolve, reject });
  11.       this.runNext();
  12.     });
  13.   }
  14.   // 执行下一个请求
  15.   runNext() {
  16.     if (this.running >= this.concurrencyLimit || this.queue.length === 0) {
  17.       return;
  18.     }
  19.     const { requestFn, resolve, reject } = this.queue.shift();
  20.     this.running++;
  21.     requestFn()
  22.       .then((result) => {
  23.         resolve(result);
  24.       })
  25.       .catch((error) => {
  26.         reject(error);
  27.       })
  28.       .finally(() => {
  29.         this.running--;
  30.         this.runNext();
  31.       });
  32.   }
  33. }
  34. // 使用示例
  35. const scheduler = new RequestScheduler(3); // 限制并发请求数量为3
  36. const createRequest = (url) => () => fetch(url).then((response) => response.json());
  37. const urls = [
  38.   'https://jsonplaceholder.typicode.com/posts/1',
  39.   'https://jsonplaceholder.typicode.com/posts/2',
  40.   'https://jsonplaceholder.typicode.com/posts/3',
  41.   'https://jsonplaceholder.typicode.com/posts/4',
  42.   'https://jsonplaceholder.typicode.com/posts/5'
  43. ];
  44. const requestPromises = urls.map((url) => scheduler.add(createRequest(url)));
  45. Promise.all(requestPromises)
  46.   .then((results) => {
  47.     console.log('所有请求完成:', results);
  48.   })
  49.   .catch((error) => {
  50.     console.error('请求失败:', error);
  51.   });
复制代码

  • createRequest方法生成返回Promise的请求函数;
  • scheduler.add方法将一个请求添加到调度器中,并在并发限制允许的情况下执行;
  • Promise.all的作用是等待所有请求完成,并且统一处理异常。
Promise.race的应用场景

Promise.race方法关注的是最快出结果(不管是fulfilled还是rejected)的promise,可以实现超时处理。
超时处理:在race中传入网络请求的promise和定时器的promise,如果网络请求在指定时间内到达则正常执行then流程,如果定时器先到达则表示超时,调用reject走catch流程。
  1. const fetchWithTimeout = (url, timeout) => {
  2.   const fetchPromise = fetch(url);
  3.   const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('请求超时')), timeout));
  4.   return Promise.race([fetchPromise, timeoutPromise]);
  5. };
  6. fetchWithTimeout('https://api.example.com/data', 5000)
  7.   .then((response) => response.json())
  8.   .then((data) => {
  9.     console.log('请求成功', data);
  10.   })
  11.   .catch((error) => {
  12.     console.error('请求失败或超时', error);
  13.   });
复制代码
Promise.allSettled

Promise.allSettled 方法返回一个在所有给定的 Promise 已经 fulfilled 或 rejected 后的 Promise,并且带有一个对象数组,每个对象表示对应的 Promise 结果。
如果是fulfilled,则结果字段为value;
如果是rejected,则结果字段为reason。
  1. const promises = [
  2.   Promise.resolve('resolved'),
  3.   Promise.reject('rejected'),
  4.   new Promise((resolve) => setTimeout(resolve, 1000, 'pending resolved'))
  5. ];
  6. Promise.allSettled(promises)
  7.   .then((results) => {
  8.     results.forEach((result) => console.log(result));
  9.   });
  10. // 输出:
  11. // { status: 'fulfilled', value: 'resolved' }
  12. // { status: 'rejected', reason: 'rejected' }
  13. // { status: 'fulfilled', value: 'pending resolved' }
复制代码
Promise.any

接受一个promise数组,返回一个promise。
和Promise.race不同,Promise.any会过滤掉所有rejected 的promise,而关注第一个fulfilled的promise的值。
如果数组中所有promise都被rejected的话,那么会返回一个AggregateError类型的实例,带有errors字段,是一个数组,指明了每一个promise的reason。
应用场景:any可以用来在多个备用资源中获取最先成功响应的资源。
最快成功返回的备用资源:假设一个数据有多个可用来源,我们只需要拿到其中一个成功响应就可以了,那么肯定是想要拿最快返回的那一个,这个时候用any就很nice~
  1. const loadImage = (url) => new Promise((resolve, reject) => {
  2.   const img = new Image();
  3.   img.onload = () => resolve(url);
  4.   img.onerror = () => reject(new Error(`Failed to load image at ${url}`));
  5.   img.src = url;
  6. });
  7. const imageUrls = ['image1.png', 'image2.png', 'image3.png'];
  8. const imagePromises = imageUrls.map(loadImage);
  9. Promise.any(imagePromises)
  10.   .then((result) => {
  11.     console.log('第一个加载完成的图片', result);
  12.   })
  13.   .catch((error) => {
  14.     console.error('所有图片加载失败', error);
  15.   });
复制代码
Promise.withResolvers

这个方法返回一个新的promise对象和用于解决或拒绝它的resolve和reject方法。
可以简单地使用Promise手动实现:
  1. Promise.withResolvers = function() {
  2.   let resolve, reject;
  3.   const promise = new Promise((res, rej) => {
  4.     resolve = res;
  5.     reject = rej;
  6.   });
  7.   return { promise, resolve, reject };
  8. };
复制代码
使用 Promise.withResolvers() 关键的区别在于解决和拒绝函数现在与 Promise 本身处于同一作用域,而不是在执行器中被创建和一次性使用。
通常在一些重复事件中使用,例如在处理流数据或者队列的时候,在这些场景下通常可以减少嵌套,优化代码结构。
这里介绍MDN上面的案例:将流转换为异步可迭代对象。
  1. // 定义 async generator 函数 readableToAsyncIterable,将流转换为异步可迭代对象
  2. async function* readableToAsyncIterable(stream) {
  3.   // 创建 Promise 和解析器对象
  4.   let { promise, resolve, reject } = Promise.withResolvers();
  5.   // 监听流的错误事件,一旦出错则调用 reject 方法
  6.   stream.on("error", (error) => reject(error));
  7.   // 监听流的结束事件,一旦结束则调用 resolve 方法
  8.   stream.on("end", () => resolve());
  9.   // 监听流的可读事件,一旦流准备好可以读取则调用 resolve 方法
  10.   stream.on("readable", () => resolve());
  11.   // 循环处理流中的数据块,直到流不再可读
  12.   while (stream.readable) {
  13.     // 等待当前的 Promise 解决
  14.     await promise;
  15.     let chunk;
  16.     // 循环读取流中的数据块
  17.     while ((chunk = stream.read())) {
  18.       // 生成数据块
  19.       yield chunk;
  20.     }
  21.     // 获取新的 Promise 和解析器对象,以便下一轮循环使用
  22.     ({ promise, resolve, reject } = Promise.withResolvers());
  23.   }
  24. }
复制代码
创建一个简单的可读流测试一下:
  1. const { Readable } = require('stream');
  2. // 测试函数
  3. async function testReadableToAsyncIterable() {
  4.   // 创建一个简单的可读流
  5.   const data = ['Hello', 'World'];
  6.   const readableStream = Readable.from(data);
  7.   // 将可读流转换为异步可迭代对象
  8.   const asyncIterable = readableToAsyncIterable(readableStream);
  9.   // 使用 for await...of 循环遍历异步可迭代对象中的数据块,并验证结果
  10.   let result = '';
  11.   for await (const chunk of asyncIterable) {
  12.     result += chunk.toString();
  13.   }
  14.   // 断言结果是否符合预期
  15.   if (result === data.join('')) {
  16.     console.log('测试通过:数据正常读取和处理。');
  17.   } else {
  18.     console.error('测试失败:数据读取和处理出现问题。');
  19.   }
  20. }
  21. // 执行测试函数
  22. testReadableToAsyncIterable();
复制代码
Promise规范与手写Promise



来源:https://www.cnblogs.com/feixianxing/p/18286001/javascript-promise
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具