Promise A+

概念

不仅是一种异步解决的方案,也是一种规范。

了解promise流程,并自己实现一个promise

文档地址:

https://promisesaplus.com/

解读

术语:

  • then()必须遵守规范
  • thenable是一个对象或函数并定义一个方法
  • value 值是JavaScript的值包括undefined/thenable/promise
  • exception另外 是一个抛出的异常
  • reason一个指示为什么造成失败或拒绝的值

要求:

  • Promise有三个状态(等待pending, 被满足fulfilled, 拒绝reject)

    • pending等待态 可变成功状态或失败状态
    • fulfilled成功态 不能转换为其他状态,必须有值且不能更改
    • reject失败态 不能转换为其他状态,必须有值且不能更改
  • 必须提供一个then()方法去访问它当前值/最终值/原因,then()方法接收两个参数(均为函数):

    • onFulfilled: 必须作为函数被调用(不可用this代替),不是函数时会被忽略掉,必须为异步不阻塞程序向下执行
    • onRejected: 必须作为函数被调用(不可用this代替),不是函数时会被忽略掉 ,必须为异步不阻塞程序向下执行
    • 以上两个参数都是可选填写
    • 如果参数是函数时,在fulfilled状态时才能执行且只能执行一次
    • 传值: resolve(123)该传值是promise.then(res => console.log(res))then()的第一个参数里函数里的形参
  • then()可以被多次调用,链式调用,调用时里面的函数参数是依次顺序调用

    • promise是成功态的时候,所有各自的onFulfilled的回调函数必须按原有的调用顺序来执行
    • promise是失败态的时候,所有各自的onRejected的回调函数必须按原有的调用顺序来执行
  • then()必须返回一个promise, 即let promise2 = promise1.then(...)

    • onFulfilled/onRejected函数执行完毕返回一个x, 即let x = onFulfilled(value);
    • x有可能是普通值(需要resolve()抛出), 也有可能是一个promise
    • 无论是成功还是失败的回调遇到抛出异常时,promise2里必须执行reject()并返回原因
  • resolvePromise函数:

    • 如果promisex指向同一个引用,必须返回一个带有reject原因的promise,并报错TypeError: Chaining cycle detected for pormise死循环
    • x是一个对象或函数时:将then赋值为x.then,执行then改变指向为x即实例化对象,且带有两个回调函数
    • x是不是一个对象或函数时:返回普通值
    • resolve/reject 只能2选1执行

关于执行器:

  1. /**
  2. * Promise(){}
  3. * 参数:@excutor 执行器 自动执行的回调函数
  4. */
  5. let promise = new Promise(//excutor (resolve, reject) => { });

实现

功能一

resolve/reject/exception/指定状态/抛出异常

  1. //index.js
  2. //使用
  3. const MyPromise = require('./MyPromise');
  4. let promise = new MyPromise((resolve, reject) => {
  5. // resolve('success');
  6. // reject('error');
  7. //另外抛出的错误 exception
  8. // throw new Error('Exception: Error');
  9. });
  10. promise.then(
  11. //onFulfilled
  12. (value) => {
  13. console.log('Fulfilled: ' + value);
  14. },
  15. //onRejected
  16. (reason) => {
  17. console.log('Rejected: ' + reason);
  18. }
  19. );
  1. //MyPromise.js
  2. //定义三种状态
  3. const PENDING = 'PENDING',
  4. FULFILLED = 'FULFILLED',
  5. REJECTED = 'REJECTED';
  6. class MyPromise {
  7. //excutor执行器 自动执行的回调函数
  8. constructor(executor) {
  9. //初始化状态
  10. this.status = PENDING;
  11. //初始化value/reason
  12. //resolve()执行前是undefined
  13. this.value = undefined;
  14. this.reason = undefined;
  15. //excutor都有自己的resolve和reject函数
  16. const resolve = (value) => {
  17. //pending可变成功状态
  18. if (this.status === PENDING) {
  19. this.status = FULFILLED;
  20. this.value = value;
  21. }
  22. };
  23. const reject = (reason) => {
  24. //pending可变失败状态
  25. if (this.status === PENDING) {
  26. this.status = REJECTED;
  27. this.reason = reason;
  28. }
  29. };
  30. //捕获 new Promise时抛出的异常
  31. try {
  32. executor(resolve, reject);
  33. } catch (e) {
  34. //当捕获到时走reject函数
  35. reject(e);
  36. }
  37. //当实例化MyPromise时立即执行
  38. //executor()里的两个参数均为函数
  39. executor(resolve, reject);
  40. }
  41. //定义then()方法 可多次调用执行
  42. //onFulfilled 成功时的回调
  43. //onRejected 失败时的回调
  44. then(onFulfilled, onRejected) {
  45. // console.log(this.status);
  46. if (this.status === FULFILLED) {
  47. onFulfilled(this.value);
  48. }
  49. if (this.status === REJECTED) {
  50. onRejected(this.reason);
  51. }
  52. }
  53. }
  54. module.exports = MyPromise;

问题1:为什么抛出异常时程序不会走onRejected函数?

executor函数执行后抛出的异常

问题2:如何实现抛出异常时程序会走onRejected函数?

可以在MyPromise内部定义try..catch来捕获异常

  1. //捕获 new Promise时抛出的异常
  2. try {
  3. executor(resolve, reject);
  4. } catch (e) {
  5. //当捕获到时走reject函数
  6. reject(e);
  7. }

功能二

依次执行promise.then()写法/链式调用

原生Promise链式调用的特点:

  1. 通过return来传递结果
  2. 通过新的promise resolve结果
  3. 通过新的promise reject原因
  4. then()走了失败的回调函数后,再走then()
  5. then()中使用throw new Error()
  6. 可以用catch捕获异常

总结:

  1. catchPromise的源 码层面上就是一个then, catch也是遵循then的运行原则
  2. 成功的条件:

    • then return一个普通的JavaScript
    • then return新的promise成功态的结果
  3. 失败的条件:

    • then return新的promise失败态的原因
    • then throw抛出了异常
  4. promise链式调用的原理是返回了一个新的Promise类 (then不具备this)
  1. //1.通过return来传递结果
  2. promise.then((res) => { return res; })
  3. .then((res) => { console.log(res); })
  4. //2.通过新的promise resolve结果
  5. promise.then((res) => { return res; })
  6. .then((res) => { return new Promise((resolve, reject) => { resolve(res); }) })
  7. .then((res) => { console.log(res); })
  8. //3.通过新的promise reject原因
  9. promise.then((res) => { return res; })
  10. .then((res) => { return new Promise((resolve, reject) => { reject('ERROR'); }) })
  11. .then(
  12. (res) => { console.log(res); },
  13. (err) => { console.log('Reject:' + err); },
  14. )
  15. //4.then()走了失败的回调函数后,再走then()的结果是什么?
  16. //没有return 值默认为undefined
  17. promise.then((res) => { return res; })
  18. .then((res) => { return new Promise((resolve, reject) => { reject('ERROR'); }) })
  19. .then(
  20. (res) => { //默认return undefined; console.log(res); },
  21. (err) => { console.log('Reject:' + err); },
  22. )
  23. .then(
  24. (value) => { console.log('Fulfilled: ' + value); }, //打印Fulfilled: undefined
  25. (reason) => { console.log('Reject:' + reason); },
  26. );
  27. //5.then()中使用throw new Error()会是怎么样的结果?
  28. //出现抛出异常的结果
  29. promise.then((res) => { return res; })
  30. .then((res) => { return new Promise((resolve, reject) => { reject('ERROR'); }) })
  31. .then(
  32. (res) => { console.log(res); },
  33. (err) => { console.log('Reject:' + err); },
  34. )
  35. .then((value) => { throw new Error('Throw Error'); })
  36. .then((value) => { console.log(value) }, (reason) => { console.log('Exeption: ' + reason) });
  37. //6.可以用catch捕获异常
  38. //没有写失败的回调时候程序走catch Error
  39. //有写失败的回调时候程序走失败的回调
  40. promise.then((res) => { return res; })
  41. .then((res) => { return new Promise((resolve, reject) => { reject('ERROR'); }) })
  42. .then(
  43. (res) => { console.log(res); },
  44. (err) => { console.log('Reject:' + err); },
  45. )
  46. .then((value) => { throw new Error('Throw Error'); })
  47. .then((value) => { console.log(value) })
  48. .catch((err) => { console.log('Catch: ' + err)});

如果then()里含有异步函数会等待执行

  1. //如何实现如果then()里含有异步函数会等待执行?
  2. promise.then((res) => { return res; })
  3. .then((res) => {
  4. return new Promise((resolve, reject) => {
  5. setTimeout(() => {
  6. resolve(res);
  7. }, 2000);
  8. });
  9. })
  10. .then((res) => { console.log(res); })
  11. //为什么遇到异步程序时不打印结果的原因:
  12. //exector执行完毕 -> 状态没有改变(PENDING) -> 导致then(()=>{},()=>{})无法判断走哪个程序 -> 不打印任何数据
  13. //实现原理:
  14. 如果程序走到then()里是PENDING状态时 -> 发布订阅模式 -> 订阅者收集所有的onFulfilled/onRejected的回调函数 -> 定义自己的resolve函数时发布 -> resolve()执行 -> 执行容器内所有的onFulfilled/onRejected方法 -> 实现异步等待时间加载
  15. class MyPromise {
  16. //excutor执行器 自动执行的回调函数
  17. constructor(executor) {
  18. ...
  19. //初始化收集所有的onFulfilled/onRejected的回调函数的容器
  20. this.onFulfilledCallbacks = [];
  21. this.onRejectedCallbacks = [];
  22. const resolve = (value) => {
  23. //pending可变成功状态
  24. if (this.status === PENDING) {
  25. this.status = FULFILLED;
  26. this.value = value;
  27. //发布模式:
  28. this.onFulfilledCallbacks.forEach((fn) => {
  29. fn();
  30. })
  31. }
  32. };
  33. const reject = (reason) => {
  34. //pending可变失败状态
  35. if (this.status === PENDING) {
  36. this.status = REJECTED;
  37. this.reason = reason;
  38. //发布模式:
  39. this.onRejectedCallbacks.forEach((fn) => {
  40. fn();
  41. })
  42. }
  43. };
  44. ...
  45. }
  46. //onFulfilled 成功时的回调
  47. //onRejected 失败时的回调
  48. then(onFulfilled, onRejected) {
  49. ...
  50. //订阅过程
  51. if (this.status === PENDING) {
  52. //订阅模式:收集所有的onFulfilled/onRejected的回调函数到容器里
  53. //数组里存放的是 仍未执行调用的函数体()=>{...}
  54. this.onFulfilledCallbacks.push(() => {
  55. //因为需要传参this.value 所以用函数嵌套的方式写
  56. onFulfilled(this.value);
  57. });
  58. this.onRejectedCallbacks.push(() => {
  59. onRejected(this.reason);
  60. });
  61. }
  62. }
  63. }
  1. //实现功能二:链式调用的实现
  2. function resolvePromise(promise2, x, resolve, reject) {
  3. console.log(promise2, x);
  4. }
  5. class MyPromise {
  6. constructor(executor) {
  7. ...
  8. };
  9. }
  10. //定义then()方法 可多次调用执行
  11. then(onFulfilled, onRejected) {
  12. //实现:then()返回一个新的Promise
  13. //onFulfilled/onRejected函数执行完毕返回一个x `let x = onFulfilled(value);`
  14. //x有可能是普通值(需要抛出), 也有可能是一个promise
  15. let promise2 = new MyPromise((resolve, reject) => {
  16. if (this.status === FULFILLED) {
  17. //必须为异步程序且不阻塞程序向下执行
  18. setTimeout(() => {
  19. //无论是成功还是失败的回调遇到抛出异常时,promise2里必须执行`reject()`并返回原因
  20. try {
  21. //如有new Error()异常,捕获它
  22. //普通值x需要处理且不能直接resolve()抛出,因为会影响程序后面返回new Promise()的执行
  23. let x = onFulfilled(this.value);
  24. /**
  25. * 专门处理x的函数
  26. * resolvePromise()
  27. * 参数1:@promise2 被抛出的promise2的成功/失败是未知的
  28. * 参数2:@x onFulfilled() 返回的值有可能是普通值/promise
  29. * 参数3: @resolve 因为外部无法访问内部的resolve所以需传入
  30. * 参数4: @reject 因为外部无法访问内部的reject所以需传入
  31. */
  32. resolvePromise(promise2, x, resolve, reject);
  33. } catch (e) {
  34. reject(e);
  35. }
  36. }, 0);
  37. }
  38. if (this.status === REJECTED) {
  39. setTimeout(() => {
  40. try {
  41. let x = onRejected(this.reason);
  42. resolvePromise(promise2, x, resolve, reject);
  43. } catch (e) {
  44. reject(e);
  45. }
  46. }, 0);
  47. }
  48. if (this.status === PENDING) {
  49. this.onFulfilledCallbacks.push(() => {
  50. try {
  51. let x = onFulfilled(this.value);
  52. resolvePromise(promise2, x, resolve, reject);
  53. } catch (e) {
  54. reject(e);
  55. }
  56. });
  57. this.onRejectedCallbacks.push(() => {
  58. try {
  59. let x = onRejected(this.reason);
  60. resolvePromise(promise2, x, resolve, reject);
  61. } catch (e) {
  62. reject(e);
  63. }
  64. });
  65. }
  66. });
  67. return promise2;
  68. }
  69. }
  70. module.exports = MyPromise;

功能三

报错/处理x值/返回普通值或promise

  1. //原生Promise分析
  2. let promise1 = new Promise((resolve, reject) => {
  3. resolve('promise1');
  4. });
  5. let promise2 = promise1.then(() => {
  6. return 1;
  7. }, (reason) => {
  8. return reason;
  9. });
  10. promise2.then((value) => {
  11. console.log(value);
  12. }, (reason) => {
  13. console.log(reason);
  14. });
  1. //实现报错:
  2. function resolvePromise(promise2, x, resolve, reject) {
  3. //如果promise和x指向同一个引用,必须返回一个带有reject原因的promise
  4. //并报错`TypeError: Chaining cycle detected for pormise`死循环
  5. if (x === promise2) {
  6. return reject(new TypeError('Chaining cycle detected for pormise'));
  7. }
  8. }
  1. //返回普通值或promise
  2. function resolvePromise(promise2, x, resolve, reject) {
  3. //如果promise和x指向同一个引用,必须返回一个带有reject原因的promise
  4. //并报错`TypeError: Chaining cycle detected for pormise`死循环
  5. if (x === promise2) {
  6. return reject(new TypeError('Chaining cycle detected for pormise'));
  7. }
  8. //resolve/reject 只能2选1执行
  9. let called = false;
  10. //当x是对象或函数时
  11. if (typeof x === 'object' && x !== null || typeof x === 'function') {
  12. //仍需捕获
  13. try {
  14. let then = x.then;
  15. //如果then是一个函数证明x里有一个then方法
  16. //说明这里面也是一个Promise
  17. if (typeof then === 'function') {
  18. //是promise
  19. //指向x promise实例对象
  20. //文档命名:y 成功函数
  21. //文档命名:r 失败函数
  22. then.call(x, (y) => {
  23. //如果调用过了,中止
  24. if (called) return;
  25. called = true;
  26. resolvePromise(promise2, y, resolve, reject);
  27. }, (r) => {
  28. if (called) return;
  29. called = true;
  30. //多层嵌套时需要递归
  31. reject(r);
  32. });
  33. } else {
  34. //不是promise时返回一个普通值
  35. resolve(x);
  36. }
  37. } catch (e) {
  38. if (called) return;
  39. called = true;
  40. reject(e);
  41. }
  42. } else {
  43. //不是对象或函数时返回一个普通值
  44. resolve(x);
  45. }
  46. }
  1. //实现then().then().then()... 链式调用时没有参数可以穿透执行得到结果
  2. then(onFulfilled, onRejected) {
  3. //实现穿透 给默认值
  4. //是函数是用该函数,不是函数时给一个新的函数,值是value / reason直接抛出
  5. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  6. onRejected = typeof onRejected === 'function' ? onRejected : reason => {
  7. throw reason
  8. };
  9. ...
  10. }
  1. //实现 catch()语法时也能拿到错误原因
  2. catch (errorCallback) {
  3. //跟then几乎一样,但第一个参数是null不用填
  4. //说明catch只是then的一个语法糖
  5. return this.then(null, errorCallback);
  6. }

实现resolvereject的静态方法

  1. Promise.resolve();
  2. Promise.reject();
  1. //实现
  2. class MyPromise {
  3. ...
  4. static resolve(value) {
  5. return new MyPromise((resolve, reject) => {
  6. resolve(value);
  7. });
  8. }
  9. static reject(error) {
  10. return new MyPromise((resolve, reject) => {
  11. reject(error);
  12. });
  13. }
  14. }

静态方法

Promise.all()

是一个静态方法

情景:通过readFile API 读取多个文件 会存在嵌套的问题,且代码不内聚

  1. readFile('./data1.json').then((res) => {
  2. dataArr.push(res);
  3. readFile('./data2.json').then((res) => {
  4. dataArr.push(res);
  5. ...
  6. });
  7. });

Promise.all()方法可以解决上述问题

  1. //写法
  2. Promise.all(
  3. [
  4. 1,
  5. readFile('./data1.json'),
  6. readFile('./data2.json'),
  7. ]
  8. ).then((res) => {
  9. console.log(res);
  10. });

原理:

遇到普通值 -> 直接处理 / 遇到promise-> 走promise程序 -> resolve/reject

弊端:

单一遇到错误,不会返回结果,只有数组中的每一个promise必须全部成功才能返回结果

重写Promise.all()静态方法

  1. class MyPromise {
  2. ...
  3. static all(promiseArr) {
  4. let resArr = [],
  5. idx = 0;
  6. return new MyPromise((resolve, reject) => {
  7. promiseArr.map((promise, index) => {
  8. if (isPromise(promise)) {
  9. promise.then((res) => {
  10. formatResArr(res, index, resolve);
  11. }, reject);
  12. } else {
  13. formatResArr(res, index, resolve);
  14. }
  15. });
  16. });
  17. function formatResArr(value, index, resolve) {
  18. resArr[index] = value;
  19. //这里idx自增 是为了防止有异步promise返回时间不确定,也有可能出现并发的情况
  20. if (++idx === promiseArr.length) {
  21. resolve(resArr);
  22. }
  23. }
  24. function isPromise(x) {
  25. if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
  26. let then = x.then;
  27. return typeof then === 'function';
  28. }
  29. return false;
  30. }
  31. }
  32. }

Promise.allSettled()

类似Promise.all()方法,返回的是多个promise集合的数组结果,但区别于的是,它可以返回错误和成功的结果

  1. Promise.allSettled([p1,p2].then((res) => {
  2. console.log(res);
  3. }))
  4. //打印
  5. [
  6. { status: 'fulfilled', value: 'success' },
  7. { status: 'rejected', value: 'error' }
  8. ]

重写Promise.allSettled()静态方法

  1. class MyPromise {
  2. ...
  3. static allSettled(promiseArr) {
  4. let resArr = [],
  5. idx = 0;
  6. //判断promiseArr是否是可迭代对象
  7. if (!isIterable(promiseArr)) {
  8. throw new TypeError('MyError: ' + promiseArr + ' is not iterable (cannot read property Symbol(Symbol.iterator))');
  9. }
  10. return new Promise((resolve, reject) => {
  11. //如果第一个参数为空的情况
  12. if (promiseArr.length === 0) {
  13. resolve([]);
  14. }
  15. promiseArr.map((promise, index) => {
  16. if (isPromise(promise)) {
  17. promise.then((value) => {
  18. formatResArr('fulfilled', value, index, resolve);
  19. }, (reason) => {
  20. formatResArr('rejected', reason, index, resolve);
  21. })
  22. } else {
  23. formatResArr('fulfilled', promise, index, resolve);
  24. }
  25. });
  26. });
  27. function formatResArr(status, value, index, resolve) {
  28. //status -> 成功/失败
  29. switch (status) {
  30. case 'fulfilled':
  31. resArr[index] = {
  32. status,
  33. value
  34. }
  35. break;
  36. case 'rejected':
  37. resArr[index] = {
  38. status,
  39. reason: value
  40. }
  41. break;
  42. default:
  43. break;
  44. }
  45. if (++idx === promiseArr.length) {
  46. resolve(resArr);
  47. }
  48. }
  49. function isIterable(value) {
  50. return value !== null && value !== undefined && typeof value[Symbol.iterator] === 'function';
  51. }
  52. }
  53. }

Promise.race()

谁先有结果,得到先到的结果

  1. let p1 = new Promise((resolve, reject) => {
  2. resolve('success');
  3. });
  4. let p2 = new Promise((resolve, reject) => {
  5. reject('error');
  6. });
  7. //Promise.race(可迭代的对象或数组)
  8. Promise.race([p1, p2, 'other']).then((res) => {
  9. console.log(res);
  10. });

实现

  1. My Promise{
  2. ...
  3. static race(promiseArr){
  4. return new MyPromise((resolev, reject) => {
  5. promiseArr.map((promise) => {
  6. if(isPromise(promise)){
  7. //直接then 拿到resolve/reject结果
  8. promise.then(resolve, reject);
  9. }else{
  10. //普通值直接抛出
  11. resolve(promise);
  12. }
  13. });
  14. });
  15. }
  16. }

Promise.finally()

  1. 无论外面Promise成功还是失败 都走 并且回调不带参数
  2. 正常走finally之后then或者 catch
  3. 如果finally内部有promise并且有延时处理 整个finally会等待
  4. 如果两个都成功走外面 取外面结果
  5. 如果外面是成功 里面是失败 取里面失败结果
  6. 如果外面是失败 里面是成功 取外面失败结果
  7. 如果外面是失败 里面是失败 取里面失败结果
  8. 如果外面是成功 里面是成功 取外面成功结果
  1. Promise.reject('result1').finally(() => {
  2. return new Promise(resolve, reject) => {
  3. setTimeout(() => {
  4. resolve('new Promise error');
  5. }, 2000);
  6. }
  7. });
  1. //实现MyFinally
  2. My Promise{
  3. ...
  4. MyFinally(finallyCallback){
  5. return this.then((value) => {
  6. return MyPromise.resolve(finallyCallback()).then(() => value);
  7. }, (reason) => {
  8. return MyPromise.resolve(finallyCallback()).then(() => {
  9. throw reason;
  10. });
  11. });
  12. }
  13. }

promisify

node开发环境中会经常使用到promisify, 因为node Api 属于回调式的,希望把代码转为promisify格式作为内置调用的一种形式

  1. //模拟fs
  2. const fs = require('fs');
  3. fs.readFile('./data/user.json', 'utf8', (err, data) => {});
  1. //node内置API promises属性
  2. const fs = require('fs').promises;
  3. fs.readFile('./data/user.json', 'utf8').then((res) => {},(err) => {});
  1. //bluebird库 异步处理方法 promisify()
  2. const readFile = bluebird.promisify(fs.readFile);
  3. readFile('./data/user.json', 'utf8').then((res) => {},(err) => {});
  1. //node工具库 util util.promisify()
  2. const util = require('util');
  3. const readFile = util.promisify(fs.readFile);
  4. readFile('./data/user.json', 'utf8').then((res) => {},(err) => {});

模拟实现util里面的promisify()方法 实现异步处理

  1. module.exports = {
  2. //接收函数方法 如fs.readFile
  3. promisify(fn){
  4. //返回一个函数 如readFile(promise).then()
  5. //...args形参 => './data/user.json', 'utf8'
  6. return function(...args){
  7. return new Promise((resolve, reject) => {
  8. //fs.readFile
  9. //fn()执行后才能then()
  10. //参数2:原生带有一个自定义的回调函数
  11. fn(...args, (error, data) => {
  12. if(error){
  13. return reject(error);
  14. }
  15. resolve(data);
  16. });
  17. })
  18. }
  19. }
  20. }

promisifyAll

把一个对象里的所有方法全部promisify一遍

  1. module.exports = {
  2. promisify(fn){...},
  3. promisifyAll(fns){
  4. //遍历每一个方法且加上async
  5. // Object.keys()遍历出自身的可枚举的键名(不含继承属性)
  6. Object.keys(fns).map((fnName) => {
  7. //有可能不是方法而是属性,需排除
  8. if(typeof fns[fnName] === 'function'){
  9. //1.更改名称
  10. fns[fnName + 'Async'] = this.promisify(fns[fnName]);
  11. }
  12. });
  13. //返回包装后的fns
  14. return fns;
  15. }
  16. }

源码

PromiseA+案例所有源码:

  1. //定义三种状态
  2. const PENDING = 'PENDING',
  3. FULFILLED = 'FULFILLED',
  4. REJECTED = 'REJECTED';
  5. function resolvePromise(promise2, x, resolve, reject) {
  6. //如果promise和x指向同一个引用,必须返回一个带有reject原因的promise
  7. //并报错`TypeError: Chaining cycle detected for pormise`死循环
  8. if (x === promise2) {
  9. return reject(new TypeError('Chaining cycle detected for pormise'));
  10. }
  11. //resolve/reject 只能2选1执行
  12. let called = false;
  13. //当x是对象或函数时
  14. if (typeof x === 'object' && x !== null || typeof x === 'function') {
  15. //仍需捕获
  16. try {
  17. let then = x.then;
  18. //如果then是一个函数证明x里有一个then方法
  19. //说明这里面也是一个Promise
  20. if (typeof then === 'function') {
  21. //是promise
  22. //指向x promise实例对象
  23. //文档命名:y 成功函数
  24. //文档命名:r 失败函数
  25. then.call(x, (y) => {
  26. //如果调用过了,中止
  27. if (called) return;
  28. called = true;
  29. resolvePromise(promise2, y, resolve, reject);
  30. }, (r) => {
  31. if (called) return;
  32. called = true;
  33. //多层嵌套时需要递归
  34. reject(r);
  35. });
  36. } else {
  37. //不是promise时返回一个普通值
  38. resolve(x);
  39. }
  40. } catch (e) {
  41. if (called) return;
  42. called = true;
  43. reject(e);
  44. }
  45. } else {
  46. //不是对象或函数时返回一个普通值
  47. resolve(x);
  48. }
  49. }
  50. function isPromise(x) {
  51. if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
  52. let then = x.then;
  53. return typeof then === 'function';
  54. }
  55. return false;
  56. }
  57. function isIterable(value) {
  58. return value !== null && value !== undefined && typeof value[Symbol.iterator] === 'function';
  59. }
  60. class MyPromise {
  61. //excutor执行器 自动执行的回调函数
  62. constructor(executor) {
  63. //初始化状态
  64. this.status = PENDING;
  65. //初始化value/reason
  66. //resolve()执行前是undefined
  67. this.value = undefined;
  68. this.reason = undefined;
  69. //初始化收集所有的onFulfilled/onRejected的回调函数的容器
  70. this.onFulfilledCallbacks = [];
  71. this.onRejectedCallbacks = [];
  72. //excutor都有自己的resolve和reject函数
  73. const resolve = (value) => {
  74. if (value instanceof MyPromise) {
  75. value.then((x) => {
  76. resolve(x);
  77. }, reject);
  78. return;
  79. }
  80. //pending可变成功状态
  81. if (this.status === PENDING) {
  82. this.status = FULFILLED;
  83. this.value = value;
  84. //发布模式:
  85. this.onFulfilledCallbacks.forEach((fn) => {
  86. fn();
  87. })
  88. }
  89. };
  90. const reject = (reason) => {
  91. //pending可变失败状态
  92. if (this.status === PENDING) {
  93. this.status = REJECTED;
  94. this.reason = reason;
  95. //发布模式:
  96. this.onRejectedCallbacks.forEach((fn) => {
  97. fn();
  98. })
  99. }
  100. };
  101. //捕获 new Promise时抛出的异常
  102. try {
  103. executor(resolve, reject);
  104. } catch (e) {
  105. //当捕获到时走reject函数
  106. reject(e);
  107. }
  108. //当实例化MyPromise时立即执行
  109. //executor()里的两个参数均为函数
  110. executor(resolve, reject);
  111. }
  112. //定义then()方法 可多次调用执行
  113. //onFulfilled 成功时的回调
  114. //onRejected 失败时的回调
  115. then(onFulfilled, onRejected) {
  116. //实现穿透 给默认值
  117. //是函数是用该函数,不是函数时给一个新的函数,值是value / reason直接抛出
  118. onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  119. onRejected = typeof onRejected === 'function' ? onRejected : reason => {
  120. throw reason
  121. };
  122. //实现:then()返回一个新的Promise
  123. //onFulfilled/onRejected函数执行完毕返回一个x `let x = onFulfilled(value);`
  124. //x有可能是普通值(需要抛出), 也有可能是一个promise
  125. let promise2 = new MyPromise((resolve, reject) => {
  126. if (this.status === FULFILLED) {
  127. //必须为异步程序且不阻塞程序向下执行
  128. setTimeout(() => {
  129. //无论是成功还是失败的回调遇到抛出异常时,promise2里必须执行`reject()`并返回原因
  130. try {
  131. //如有new Error()异常,捕获它
  132. //普通值x需要处理且不能直接resolve()抛出,因为会影响程序后面返回new Promise()的执行
  133. let x = onFulfilled(this.value);
  134. /**
  135. * 专门处理x的函数
  136. * resolvePromise()
  137. * 参数1:@promise2 被抛出的promise2的成功/失败是未知的
  138. * 参数2:@x onFulfilled() 返回的值有可能是普通值/promise
  139. * 参数3: @resolve 因为外部无法访问内部的resolve所以需传入
  140. * 参数4: @reject 因为外部无法访问内部的reject所以需传入
  141. */
  142. resolvePromise(promise2, x, resolve, reject);
  143. } catch (e) {
  144. reject(e);
  145. }
  146. }, 0);
  147. }
  148. if (this.status === REJECTED) {
  149. setTimeout(() => {
  150. try {
  151. let x = onRejected(this.reason);
  152. resolvePromise(promise2, x, resolve, reject);
  153. } catch (e) {
  154. reject(e);
  155. }
  156. }, 0);
  157. }
  158. if (this.status === PENDING) {
  159. //订阅模式:收集所有的onFulfilled/onRejected的回调函数到容器里
  160. //数组里存放的是 仍未执行调用的函数体()=>{...}
  161. this.onFulfilledCallbacks.push(() => {
  162. setTimeout(() => {
  163. try {
  164. //因为需要传参this.value 所以用函数嵌套的方式写
  165. let x = onFulfilled(this.value);
  166. resolvePromise(promise2, x, resolve, reject);
  167. } catch (e) {
  168. reject(e);
  169. }
  170. }, 0);
  171. });
  172. this.onRejectedCallbacks.push(() => {
  173. setTimeout(() => {
  174. try {
  175. let x = onRejected(this.reason);
  176. resolvePromise(promise2, x, resolve, reject);
  177. } catch (e) {
  178. reject(e);
  179. }
  180. }, 0);
  181. });
  182. }
  183. });
  184. return promise2;
  185. }
  186. catch (errorCallback) {
  187. //跟then几乎一样,但第一个参数是null不用填
  188. //说明catch只是then的一个语法糖
  189. return this.then(null, errorCallback);
  190. }
  191. static resolve(value) {
  192. return new MyPromise((resolve, reject) => {
  193. resolve(value);
  194. });
  195. }
  196. static reject(error) {
  197. return new MyPromise((resolve, reject) => {
  198. reject(error);
  199. });
  200. }
  201. static all(promiseArr) {
  202. let resArr = [],
  203. idx = 0;
  204. return new MyPromise((resolve, reject) => {
  205. promiseArr.map((promise, index) => {
  206. if (isPromise(promise)) {
  207. promise.then((res) => {
  208. formatResArr(res, index, resolve);
  209. }, reject);
  210. } else {
  211. formatResArr(res, index, resolve);
  212. }
  213. });
  214. });
  215. function formatResArr(value, index, resolve) {
  216. resArr[index] = value;
  217. if (++idx === promiseArr.length) {
  218. resolve(resArr);
  219. }
  220. }
  221. }
  222. static allSettled(promiseArr) {
  223. let resArr = [],
  224. idx = 0;
  225. //判断promiseArr是否是可迭代对象
  226. if (!isIterable(promiseArr)) {
  227. throw new TypeError('MyError: ' + promiseArr + ' is not iterable (cannot read property Symbol(Symbol.iterator))');
  228. }
  229. return new Promise((resolve, reject) => {
  230. //如果第一个参数为空的情况
  231. if (promiseArr.length === 0) {
  232. resolve([]);
  233. }
  234. promiseArr.map((promise, index) => {
  235. if (isPromise(promise)) {
  236. promise.then((value) => {
  237. formatResArr('fulfilled', value, index, resolve);
  238. }, (reason) => {
  239. formatResArr('rejected', reason, index, resolve);
  240. })
  241. } else {
  242. formatResArr('fulfilled', promise, index, resolve);
  243. }
  244. });
  245. });
  246. function formatResArr(status, value, index, resolve) {
  247. //status -> 成功/失败
  248. switch (status) {
  249. case 'fulfilled':
  250. resArr[index] = {
  251. status,
  252. value
  253. }
  254. break;
  255. case 'rejected':
  256. resArr[index] = {
  257. status,
  258. reason: value
  259. }
  260. break;
  261. default:
  262. break;
  263. }
  264. if (++idx === promiseArr.length) {
  265. resolve(resArr);
  266. }
  267. }
  268. }
  269. }
  270. module.exports = MyPromise;