JS高级 之 Promise 详解

一、异步代码的困境

1. 什么是单线程

01 - 概念

单线程 : 就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推

02 - js是单线程

js是单线程的,主要用途是与用户互动,以及操作DOM。

这决定了它只能是单线程

注 : JS的单线程并不是指整个JS引擎只有1个线程它是指运行代码只有1个线程,但是它还有其他线程来执行其他任务。比如时间函数的计时、AJAX技术中的和后台交互等操作

假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准,所以规定同时只能操作一个东西 

03 - 单线程的优点

优点 : 实现起来比较简单,执行环境相对单纯,一行一行代码执行下去即可

04 - 单线程的缺点

缺点 : 只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行

常见的情况有 : 

  • 浏览器无响应,可能程序死循环了
  • 浏览器首次加载白屏,可能网络卡请求资源太慢

2. 什么是异步

立即执行后不是马上得到结果,比如 网络请求(如ajax)、定时器(setTimeout/setInterval),在可能发生等待的情况,不能阻挡住主线程的执行,因此,所有的“等待的情况”都需要异步

3. 回调函数

01 - 概念

回调函数 : 在调用setTimeout函数时,传递了一个函数进去,这个函数并没有立即被调用,而是在5秒后被调用。这种函数也被称为回调函数

02 - 配合异步操作

如果想要拿到异步操作后的结果或者是状态,可以使用回调函数

        栗子一 🌰

function foo(fn) {
  // 2. 执行代码
  setTimeout(() => {
    let result = 100 + 200;
    // 3. 执行完成后,执行回调函数,把结果传递出去
    fn(result);
  }, 3000);
}
// 1. 传入一个函数,这个函数就叫做回调函数
foo((res) => {
  console.log(res); // 300
});

         栗子二 🌰

/**
 * counter : 传入的参数
 * successCallback : 成功执行的回调函数
 * failureCallback : 失败执行的回调函数
 */
function execCode(counter, successCallback, failureCallback) {
  // 异步任务
  setTimeout(() => {
    if (counter > 0) {
      // counter可以计算的情况
      let total = 0;
      for (let i = 0; i < counter; i++) {
        total += i;
      }
      // 在某一个时刻只需要回调传入的函数
      successCallback(total);
    } else {
      // 失败情况, counter有问题
      failureCallback(`${counter}值有问题`);
    }
  }, 3000);
}

// 传入三个参数
execCode(
  100,
  (value) => {
    console.log('本次执行成功了:', value);
  },
  (err) => {
    console.log('本次执行失败了:', err);
  }
);

缺点 : 回调函数和参数需要同时传递,还需要确保位置没错,限制比较大 

03 - 回调地狱

如果回调函数中又有回调函数,一层一层包裹下去的话~

        栗子 🌰

需求 : 假如需要在3秒后吃饭,然后2秒后喝水,4秒后跑步,1秒后拉肚子

function foo(fn) {
  // 第一步
  setTimeout(() => {
    console.log('吃饭');
    // 第二步
    setTimeout(() => {
      console.log('喝水');
      // 第三步
      setTimeout(() => {
        console.log('跑步');
        // 第四步
        setTimeout(() => {
          console.log('拉肚子');
        }, 1000);
      }, 4000);
    }, 2000);
  }, 3000);
}
foo();

这还只是在代码量少的情况下,如果每一步操作都很多很复杂,其中又包含自己的回调函数,那么代码结构可想而知,馋不忍睹

二、Promise的简单使用

1. Promise的概念

 Promise是一个类,可以翻译成 承诺、许诺 、期约

在通过new创建Promise对象时,需要传入一个回调函数,称之为executor

  • 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject
  • 调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数
  • 调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数

Executor

Executor : 是创建Promise时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数

通常是在Executor中确定Promise状态:

  • 通过resolve,可以兑现(fulfilled)Promise的状态,也可以称之为已决议(resolved)
  • 通过reject,可以拒绝(reject)Promise的状态

一旦状态被确定下来,Promise的状态会被 锁死,该Promise的状态是不可更改的

  • 在调用resolve的时候,如果resolve传入的值本身不是一个Promise,那么会将该Promise的状态变成 兑现(fulfilled)
  • 在之后再去调用reject时,已经不会有任何的响应了(并不是这行代码不会执行,而是无法改变Promise状态)

2. Promise的三种状态

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝
    • 当执行executor中的代码时,处于该状态
  • 已兑现(fulfilled): 意味着操作成功完成
    • 执行了resolve时,处于该状态,Promise已经被兑现
  • 已拒绝(rejected): 意味着操作失败
    • 执行了reject时,处于该状态,Promise已经被拒绝

Promise的状态一旦被确定下来, 就不会再更改, 也不能再执行某一个回调函数来改变状态

// 1.创建一个Promise对象
const promise = new Promise((resolve, reject) => {
  // 1.待定状态 pending
  console.log("111111")
  console.log("222222")
  console.log("333333")

  // 2.兑现状态 fulfilled
  resolve()

  // 3.拒绝状态 rejected
  reject()
})

promise.then(value => {
  console.log("成功的回调")
}).catch(err => {
  console.log("失败的回调")
})

3. 使用Promise改造栗子二

function execCode(counter) {
  // 1. 创建一个 promise 对象
  const promise = new Promise((resolve, reject) => {
    // 异步任务
    setTimeout(() => {
      if (counter > 0) {
        // counter可以计算的情况
        let total = 0;
        for (let i = 0; i < counter; i++) {
          total += i;
        }
        // 成功的回调,一执行就会马上去执行 .then 方法
        resolve(total);
      } else {
        // 失败的回调,一执行就会马上去执行 .catch 方法
        reject(`${counter}有问题`);
      }
    }, 3000);
  });
  // 2. 把该 promise 对象返回出去
  return promise;
}
// 3. 接受 promise 对象
const promise = execCode(100)
// 4. 处理成功的回调
promise.then((value) => {
  console.log("成功有了结果: ", value)
})
// 5. 处理失败的回调
promise.catch((err) => {
  console.log("失败有了错误: ", err)
})

三、在Promise中使用resolve方法

情况一:如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数

情况二:如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态

情况三:如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据then方法的结果来决定Promise的状态

情况一 : 普通的值

const promise = new Promise((resolve, reject) => {
  // 注意 : 方便演示,放到一起,其实只会执行一次resolve
  resolve(123); // 123
  resolve('123'); // '123'
  resolve({ name: 'star' }); // { name: 'star'
  resolve([1, 2, 3, 4, 5]); // [1, 2, 3, 4, 5]
});

promise.then((res) => {
  console.log(res);
});

情况二 : promise对象

const p = new Promise((resolve, reject) => {
  // 2. 会看这个promise对象中 执行的是什么状态
  resolve({ name: 'resolve' });
  // reject({ name: 'reject' });
});

const promise = new Promise((resolve, reject) => {
  // 1. 传入promise对象
  resolve(p);
});

promise
  .then((res) => {
    console.log(res); // { name: 'resolve' }
  })
  .catch((err) => {
    console.log(err); // { name: 'reject' }
  });

情况三 : thenable对象

const promise = new Promise((resolve, reject) => {
  // 1. 传入对象,但是该对象中含有then方法
  resolve({
    name: 'star',
    // 2. 会自动调用该then方法,同时传入一个resolve参数和reject
    then(resolve, reject) {
      // resolve(123);
      reject(456);
    }
  });
});

promise
  .then((res) => {
    console.log(res); // 123
  })
  .catch((err) => {
    console.log(err); // 456
  });

四、在Promise中使用reject方法

传入的参数 无论是什么形态,都会直接作为reject状态的参数传递到catch,不区分参数

const promise = new Promise((resolve, reject) => {
  // reject('failure'); // 失败的回调: failure

  // reject(
  //   new Promise((reso) => {
  //     reso('123');
  //   })
  // ); // 失败的回调: Promise {<fulfilled>: '123'}
  reject({
    then(res, rej) {}
  }); // {then: ƒ}
});

promise.catch((err) => {
  console.log('失败的回调:', err);
});

五、在Promise中使用then方法

1. then方法的使用

  • then方法是Promise对象上的一个方法(实例方法)
    • 其实是放在Promise的原型上的 Promise.prototype.then

then方法接受两个参数:

  • fulfilled的回调函数:当状态变成fulfilled时会回调的函数
  • reject的回调函数:当状态变成reject时会回调的函数

一个Promise的then方法是可以被多次调用的:

  • 每次调用我们都可以传入对应的fulfilled回调
  • 当Promise的状态变成fulfilled的时候,这些回调函数都会被执行
const promise = new Promise((resolve, reject) => {
  resolve('success');
});

// then参数的传递方法:
// 1. 可以传递两个参数
// promise.then(res => {
//   console.log("成功回调~", res)
// }, err => {
//   console.log("失败回调~", err)
// })
// 2.也可以分开写
promise.then((res) => {}).catch((err) => {});

// 相当于监听了多个,每个都会相应
promise.then((res) => {
  console.log('成功回调~', res);
});
promise.then((res) => {
  console.log('成功回调~', res);
});
promise.then((res) => {
  console.log('成功回调~', res);
});
promise.then((res) => {
  console.log('成功回调~', res);
});

//但是如果调用的是reject,那么必须有捕获错误的回调,否则会报错
const p = new Promise((resolve, reject) => {
  reject('fail');
});
p.then(
  (res) => {
    console.log('成功回调~', res);
  },
  (err) => {
    // 必须有
    console.log('失败回调~', err);
  }
);
// 必须有
p.catch(() => {});

2. then方法的返回值

调用then方法后,默认会返回一个promise对象,这就是可以链式调用的原因 

Promise有三种状态,那么这个Promise处于什么状态?

  • then方法中的回调函数本身在执行的时候,那么它处于pending状态
  • then方法中的回调函数返回一个结果时,那么它处于fulfilled状态,并且会将结果作为resolve的参数
    • 情况一:返回一个普通的值
    • 情况二:返回一个Promise对象
    • 情况三:返回一个thenable对象
  • 当then方法抛出一个异常时,那么它处于reject状态 
const promise = new Promise((resolve) => {
  resolve(123);
});

promise
  .then((res) => {
    console.log('first:', res); // first: 123
    // 相当于返回undefined
    // return resolve(undefined)
  })
  .then((res) => {
    // 其实内部相当于是这么做的
    return new Promise((resolve) => {
      console.log('second:', res); // second: undefined
      resolve(undefined);
    });
  })
  .then((res) => {
    console.log('three:', res); // three: undefined
  });

01 - 返回普通值

const promise = new Promise((resolve) => {
  resolve(111);
});

promise
  .then((res) => {
    console.log('first:', res); // first: 111
    return 222;
  })
  .then((res) => {
    console.log('second:', res); // second: 222
    return [111, 222];
  })
  .then((res) => {
    console.log('three:', res); // three: [111, 222]
  });

02 - 返回新的promise对象

const promise = new Promise((resolve) => {
  resolve(111);
});

promise
  .then((res) => {
    console.log('first:', res); // first: 111
    /**
     * 相当于resolve(new Promise),看上面的文章可知,当resolve一个新的promise对象时
     * 会看当前新的promise的状态值,并等待其resolve|reject的值
     */
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        // resolve(222);
        reject(333);
      }, 3000);
    });
  })
  .then((res) => {
    console.log('success:', res); // success: 222
  })
  .catch((res) => {
    console.log('fail:', res); // fail: 333
  });

03 - 返回thenable对象

const promise = new Promise((resolve) => {
  resolve(111);
});

promise
  .then((res) => {
    console.log('first:', res); // first: 111
    /**
     * 相当于resolve(thenable对象),看上面的文章可知,当resolve一个新的promise对象时
     * 会去调当前对象的then方法,并有resolve, reject作为参数
     */
    return {
      then(resolve, reject) {
        setTimeout(() => {
          resolve(222);
          // reject(333);
        }, 3000);
      }
    };
  })
  .then((res) => {
    console.log('success:', res); // success: 222
  })
  .catch((res) => {
    console.log('fail:', res); // fail: 333
  });

六、在Promise中使用catch方法

1. catch方法的使用

  • catch方法也是Promise对象上的一个方法(实例方法):
    • 它也是放在Promise的原型上的 Promise.prototype.catch

一个Promise的catch方法是可以被多次调用的:

  • 每次调用都可以传入对应的reject回调
  • 当Promise的状态变成reject的时候,这些回调函数都会被执行
const promise = new Promise((resolve, reject) => {
  reject("failure")
})

promise.then(res => {
  console.log("成功的回调:", res)
}).catch(err => {
  console.log("失败的回调:", err)
})

promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})
promise.catch(err => {
  console.log("失败的回调:", err)
})

2. catch方法的返回值

catch方法也是会返回一个Promise对象的,所以catch方法后面可以继续调用then方法或者catch方法

const promise = new Promise((resolve, reject) => {
  reject('error: aaaaa');
  // resolve('aaaaaa');
});

// 1.catch方法也会返回一个新的Promise
promise
  .catch((err) => {
    console.log('catch回调:', err);
    // 默认会返回新的promise对象,resovle('bbbbb')
    return 'bbbbb';
  })
  .then((res) => {
    console.log('then第一个回调:', res);
    return 'ccccc';
  })
  .then((res) => {
    console.log('then第二个回调:', res);
  });

3. 使用catch

一旦被reject或者报错,会去寻找最近的catch,中途不会执行then方法

const promise = new Promise((resolve, reject) => {
  reject('error: aaaaa');
});
promise
  .then(() => {
    console.log('不会执行');
    return 123;
  })
  .then(() => {
    console.log('不会执行');
  })
  // 直接找到最近的catch执行
  .catch((err) => {
    console.log('catch回调被执行:', err); // catch回调被执行: error: aaaaa
    // 默认返回undefined
  })
  // 会执行,因为catch也返回一个promise对象
  .then(() => {
    console.log('会执行!!!'); // 会执行!!!
  });

4. 中途断裂

如果中途想执行reject或者报错了 

const promise = new Promise((resolve, reject) => {
  resolve(111);
});
promise
  .then((res) => {
    console.log('then第一次回调:', res); // then第一次回调: 111
    return 222;
  })
  .then((res) => {
    console.log('then第二次回调:', res); // then第二次回调: 222
    throw new Error('第三个Promise的异常error');
  })
  .then(() => {
    console.log('不会执行');
  })
  .catch((err) => {
    console.log('catch回调被执行:', err); // catch回调被执行: 第三个Promise的异常error
    // 默认返回undefined
    return 333;
  })
  .then((res) => {
    console.log('会执行', res); // 会执行 333
    return new Promise((_, reject) => {
      reject('reject');
    });
  })
  .catch((err) => {
    console.log('catch回调被执行:', err); // catch回调被执行: reject
  });

七、在Promise中使用finally方法

finally是在ES9中新增的一个特性:表示无论Promise对象无论变成fulfilled还是rejected状态,最终都会被执行的代码

finally方法是不接收参数的,因为无论前面是fulfilled状态,还是rejected状态,它都会执行 

const promise = new Promise((resolve, reject) => {
  resolve('aaaa');
  // reject("bbbb")
});

promise
  .then((res) => {
    console.log('then:', res); // aaaa
  })
  .catch((err) => {
    console.log('catch:', err); // bbbb
  })
  .finally(() => {
    console.log('game over');
  });

七、promise的类方法 

上面文章中的then、catch、finally方法都属于Promise的实例方法

都是存放在Promise的prototype上的

1. Promise.resolve

 Promise.resolve的用法相当于new Promise,并且执行resolve操作

promise.resolve('message');

// 相当于

new Promise((resolve) => {
  resolve('message');
});

resolve参数的形态:

  • 情况一:参数是一个普通的值或者对象
  • 情况二:参数本身是Promise
  • 情况三:参数是一个thenable 

和上面的一样,就不写啦

2. Promise.reject

Promise.reject传入的参数 无论是什么形态,都会直接作为reject状态的参数传递到catch

const promise = Promise.reject("rejected error")
promise.catch(err => {
  console.log("err:", err)
})

// 相当于

new Promise((_, reject) => {
  reject("rejected error")
})

3. Promise.all

Promise..all : 将多个Promise包裹在一起形成一个新的Promise

新的Promise状态由包裹的所有Promise共同决定:

  • 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组
  • 当有一个Promise状态为rejected时,新的Promise状态为rejected,并且会将第一个reject的返回值作为参数

全为fulfilled

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p1 resolve');
  }, 3000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2 resolve');
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3 resolve');
  }, 5000);
});

// all:全部/所有
Promise.all([p1, p2, p3]).then((res) => {
  console.log('all promise res:', res); // ['p1 resolve', 'p2 resolve', 'p3 resolve']
});

有一个为rejected

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('p1 reject error');
  }, 3000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2 resolve');
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3 resolve');
  }, 5000);
});

// all:全部/所有
Promise.all([p1, p2, p3])
  .then((res) => {
    console.log('all promise res:', res);
  })
  .catch((err) => {
    // 3秒后直接来这里,其他的都不管了
    console.log('all promise err:', err); // all promise err: p1 reject error
  });

缺陷

  • 当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态
  • 对于resolved的,以及依然处于pending状态的Promise,我们是获取不到对应的结果的

4. Promise.allSettled

该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是rejected时,才会有最终的状态

并且这个Promise的结果一定是fulfilled的

// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p1 resolve")
    reject('p1 reject error');
  }, 3000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2 resolve');
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3 resolve');
  }, 5000);
});

// 一定是成功的状态,直接调用then即可
Promise.allSettled([p1, p2, p3]).then((res) => {
  // 5秒后打印
  console.log('all settled:', res);
  /**
   * 会按照传参数的顺序打印
   * [
   *    {status: 'rejected', reason: 'p1 reject error'} // 失败的信息
   *    {status: 'fulfilled', value: 'p2 resolve'} // 成功的信息
   *    {status: 'fulfilled', value: 'p3 resolve'} // 成功的信息
   * ]
   */
});

5. Promise.race

  • 如果其中一个Promise有了结果,最终的Promise的状态就是这个
  • race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果
  • 不管是成功的还是失败的
// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p1 resolve');
    // reject("p1 reject error")
  }, 3000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2 resolve');
    // reject('p2 reject error');
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3 resolve');
  }, 5000);
});

// 特点: 会等到一个Promise有结果(无论这个结果是fulfilled还是rejected)
// 因为p2只用等2秒,它最快,不管它是成功还是失败,都用p2的值
Promise.race([p1, p2, p3])
  .then((res) => {
    console.log('race promise:', res); // race promise: p2 resolve
  })
  .catch((err) => {
    console.log('race promise err:', err); // race promise err: p2 reject error
  });

6. Promise.any

  • 和race方法是类似
  • any方法会等到一个fulfilled状态,才会决定新Promise的状态
  • 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态会报一个AggregateError的错误
// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p1 resolve")
    reject('p1 reject error');
  }, 3000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p2 resolve")
    reject('p2 reject error');
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("p3 resolve")
    reject('p3 reject error');
  }, 5000);
});

// 类方法: any方法
Promise.any([p1, p2, p3])
  .then((res) => {
    // 一旦遇到一个resolve,就会来这里
    console.log('any promise res:', res);
  })
  .catch((err) => {
    // 全是reject,才会来这里
    console.log('any promise err:', err); // any promise err: AggregateError: All promises were rejected
  });

八、手写Promise

分步骤,一步一步完善代码

1. 实现状态的变化

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

/**
 * 创建一个promise
 */
class StarPromise {
  constructor(excute) {
    // 定义状态
    this._state = PENDING
    // 定义值
    this._value = undefined

    try {
      // 这里需绑定this,否则外界调用_resolve,_reject 会因为找不到this而报错
      excute(this._resolve.bind(this), this._reject.bind(this))
    } catch (error) {
      // 捕获异常,报错自动_reject
      this._reject(error)
    }
  }

  /**
   *
   * @param {string} newState 新状态
   * @param {any} value 值
   */
  _changeState(newState, value) {
    if (this._state !== PENDING) return
    this._state = newState
    this._value = value
  }

  // 成功的回调
  _resolve(data) {
    this._changeState(FULFILLED, data)
  }
  // 失败的回调
  _reject(err) {
    this._changeState(REJECTED, err)
  }
}

const starPromise = new StarPromise((resolve, reject) => {
  resolve(123)
  // throw 123
})

console.log('starPromise', starPromise) // { _state: 'fulfilled', _value: 123 }

2. 创建微任务方法 

// 创建一个微任务
const nextTick = (fn) => {
  // 是node环境
  if (process && process.nextTick) {
    process.nextTick(fn)
  } else {
    // 不是node环境,setTimeout适用于其他环境,比如浏览器
    setTimeout(fn, 0)
  }
}

3. 核心代码

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 创建一个微任务
const nextTick = (fn) => {
  // 是node环境
  if (process && process.nextTick) {
    process.nextTick(fn)
  } else {
    // 不是node环境,setTimeout适用于其他环境,比如浏览器
    setTimeout(fn, 0)
  }
}
// 判断是否是promise
const isPromise = (object) => {
  return !!(object && typeof object === 'object' && typeof object.then === 'function')
}

/**
 * 创建一个promise
 */
class StarPromise {
  constructor(excute) {
    // 定义状态
    this._state = PENDING
    // 定义值
    this._value = undefined
    // 处理函数形成的队列
    this._handles = []

    try {
      // 这里需绑定this,否则外界调用_resolve,_reject 会因为找不到this而报错
      excute(this._resolve.bind(this), this._reject.bind(this))
    } catch (error) {
      // 捕获异常,报错自动_reject
      this._reject(error)
    }
  }

  /**
   * @param {string} newState 新状态
   * @param {any} value 值
   */
  _changeState(newState, value) {
    if (this._state !== PENDING) return
    this._state = newState
    this._value = value
    // 状态变化,执行队列中的函数
    this._excuteHandles()
  }
  // 执行队列中的函数
  _excuteHandles() {
    // 如果状态是PENDING,不执行
    if (this._state === PENDING) return

    while (this._handles[0]) {
      // 取出第一个处理函数,先进先出,执行完后删除
      const handle = this._handles.shift()
      // 执行函数
      this._excuteHandle(handle)
    }
  }
  // 执行函数
  _excuteHandle({ state, fn, resolve, reject }) {
    nextTick(() => {
      // 判断状态是否与处理函数的状态一致
      if (this._state !== state) return
      // 判断是否有处理函数
      if (typeof fn !== 'function') {
        // 若不是函数,需要和之前的保持一致,沿用之前的数据
        this._state === FULFILLED ? resolve(this._value) : reject(this._value)
        return
      }
      try {
        const result = fn(this._value)
        // 判断是否是promise
        if (isPromise(result)) {
          // 这里传入的resolve和reject是新promise的resolve和reject,这样就可以实现链式调用
          result.then(resolve, reject)
        } else {
          // 不是promise,直接返回
          resolve(result)
        }
      } catch (error) {
        reject(error)
      }
    })
  }

  // 成功的回调
  _resolve(data) {
    this._changeState(FULFILLED, data)
  }
  // 失败的回调
  _reject(err) {
    this._changeState(REJECTED, err)
  }

  /**
   * 向处理队列中添加一个处理任务
   * @param {String} state 状态
   * @param {Function} fn 执行的函数
   * @param {resolve} fn 当执行的函数执行成功后的回调
   * @param {reject} fn 当函数执行失败后的回调
   */
  _pushHandle(state, fn, resolve, reject) {
    this._handles.push({ state, fn, resolve, reject })
  }

  /**
   *
   * @param {Function} onFilfilled 成功后调用的参数
   * @param {Function} onRejected 失败后调用的参数
   * @returns  {StarPromise} 返回一个新的promise
   */
  then(onFilfilled, onRejected) {
    return new StarPromise((resolve, reject) => {
      this._pushHandle(FULFILLED, onFilfilled, resolve, reject)
      this._pushHandle(REJECTED, onRejected, resolve, reject)
      this._excuteHandles()
    })
  }

  /**
   * 仅处理失败的回调,相当于then(null, onRejected)
   * @param {Function} onRejected
   */
  catch(onRejected) {
    this.then(null, onRejected)
  }
  /**
   * 无论成功或失败都会执行
   * @param {funtion} onFinally
   * @returns {StarPromise} 返回一个新的promise
   */

  finally(onFinally) {
    // 调用then方法,成功或失败都传入onFinally,这样就可以实现无论成功或失败都会执行
    return this.then(
      (data) => {
        onFinally()
        return data
      },
      (err) => {
        onFinally()
        throw err
      }
    )
  }
}

const starPromise = new StarPromise((resolve, reject) => {
  resolve(123)
  // throw 123
})

starPromise
  .then(
    (data) => {
      console.log('success', data)
      return new StarPromise((resolve, reject) => {
        resolve(456)
      })
    },
    (err) => {
      console.log('reject', err)
    }
  )
  .then((data) => {
    console.log('nextSuccess', data)
  })

4. 静态方法

  static resolve(data) {
    if (data instanceof StarPromise) {
      return data
    }
    return new StarPromise((resolve) => {
      if (isPromise(data)) {
        data.then(resolve)
      } else {
        resolve(data)
      }
    })
  }

  static reject(err) {
    return new StarPromise((resolve, reject) => {
      reject(err)
    })
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值