添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 第一是 嵌套调用 ,下面的任务依赖上个任务的请求结果,并在上个任务的回调函数内部执行新的业务逻辑,这样当嵌套层次多了之后,代码的可读性就变得非常差了。
  • 第二是任务的不确定性,执行每个任务都有两种可能的结果(成功或者失败),所以体现在代码中就需要对每个任务的执行结果做两次判断,这种对每个任务都要进行一次额外的 错误处理 的方式,明显增加了代码的混乱程度。
  • Promise 解决了什么问题呢?

    作用 :管理异步方式返回的代码

    解决的问题

  • 第一是消灭嵌套调用 -> 可以 实现链式的写法 来实现同步异步操作
  • 第二是合并多个任务的错误处理
  • Promise 优缺点

    我们可以 注册任意数量的函数 在成功或者失败后运行,也可以 在任何时候注册 事件处理程序。

    回调函数写成了 链式写法 ,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现很多强大的功能。 如果一个任务已经完成,再添加回调函数,该回调函数会立即执行

    1、无法取消 Promise 一旦新建/声明它就会立即执行 ,无法中途取消

    2、“Promise会吃掉错误” - 如果不设置 cathch/reject 回调函数, Promise 内部抛出的错误,不会反应到外部 -> 不影响 Promise 外部脚本的执行

    3、当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

    Promise 怎么解决嵌套回调问题

    1、 Promise 实现了 回调函数的延时绑定 -> 创建完 Promise 后再用 .then 方法绑定回调函数

    2、将回调函数 onResolve 的返回值穿透到最外层 -> 需要通过返回值确认创建什么类型的 Promise 任务

    Promise 怎么处理异常

    即使一个 Promise 有多个回调函数,也可以通过一个 .catch 方法来捕获异常

    –> Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被 onReject 函数处理或 catch 语句捕获为止

    1/3、Promise 特点 -> 状态凝固

    promise会一直处于等待状态,直到它所包装的异步调用返回/超时/结束。

    resolve下面的语句其实是可以执行的 ,那么为什么 reject 的状态信息在下面没有接受到呢?

    new 出一个 Promise 对象 - 初始化实例时,这个对象的起始状态就是 Pending 状态,在根据 resolve reject 返回 Fulfilled 状态/ Rejected 状态 —> resolve 与 reject 只能执行一个

    Promise 构造函数接受一个 函数作为参数 ,该函数的两个参数分别是 resolve reject 。它们是两个函数, 由 JavaScript 引擎提供 ,不用自己实现。

    new Promise((resolve, reject) => {
        var param = '一朵🌹';
        if (error) {
            reject(new Error('Promise 异步执行异常'));
        } else {
            resolve(param);
    }).then((res) => {
        console.log(res);  //  一朵🌹
        var param = '🦕🦕🦕';
        return param;
    }).then((res) => {
        console.log(res);  //  🦕🦕🦕
    }).catch((error) => {
        console.log(error); //  Promise 异步执行异常
    

    2/3、Promise 特点 - then

    then 中返回了 新的 Promise 实例,但是 then 中注册的回调仍然是属于上一个 Promise 的;

    then 函数是负责注册回调的,真正的执行是在 Promise 的状态被改变之后

    【补充】:

    catch 方法是 then(null, rejection) 的别名 -> 也就是说 catch 方法也会 返回一个新的 Promise 实例

    p.then((val) => console.log('fulfilled:', val))
        .catch((err) => console.log('rejected', err));
    // 等同于
    p.then((val) => console.log('fulfilled:', val))
        .then(null, (err) => console.log("rejected:", err));
    

    3/3、Promise 特点 - finally

    不管 Promise 最后的状态如何,都要执行一些最后的操作

    finally 回调函数不接受任何参数 - 我们把这些操作放到 finally 中,也就是说 finally 注册的函数是与 Promise 的状态无关的,不依赖 Promise 的执行结果。

    promise
      .finally(() => {
      	// 语句
    

    Promise 为什么要使用微任务

    我们先来简单实现一个Promise -> 仅为展示微任务相关,不作为 Promise 的简单代码实现

    因为定时器的执行效率不是太高,采用微任务实现了回调函数延迟绑定,这就是为什么Promise使用微任务的原由了

    下文开始为 Promise 基础,参考博客 阮一峰-ES6

    Promise 实现图片加载

    var preloadImage =function(path){
        return new Promise(function(resolve, reject){
            var image =new Image(); // 实例
            image.onload  = resolve; // 监听属性1 - 在图片加载成功后调用
            image.onerror = reject; // 监听属性2 - 在加载失败调用
            image.src = path;
    preloadImage('https://example.com/my.jpg')
        .then(function(e){ document.body.append(e.target)})
        .then(function(){ console.log('加载成功')})
    

    Promise 返回另一个 Promise 时,状态传递

    const p1 = new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('fail')), 3000);
    const p2 = new Promise(function (resolve, reject) {
        setTimeout(() => resolve(p1), 1000);
        .then(result => console.log(result))
        .catch(error => console.log(error))
    // Error: fail
    

    上面代码详解:

    p1 是一个 Promise,3秒后变为 reject;p2 的状态在 1秒后改变,resolve 返回 p1;

    由于 p2 返回的是另一个 Promise,导致 p2 自己的状态无效 了, p2 的状态由 p1 决定;

    p2 后面注册的 then 语句都变成针对 p1的 -> 再过 2秒, p1 变成 rejected ,导致触发 catch 指定的回调。

    Promise.all

    Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

    const p = Promise.all([p1, p2, p3]);
      
  • 1、只有 p1、p2、p3 的状态 都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数。
  • 2、只要 p1、p2、p3 有一个被 rejected ,p 的状态就会变成 rejected ,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数
  • 3、如果作为参数的 Promise 实例,自己定义了 catch 方法,那么它一旦被 rejected,并不会触发 Promise.all()catch 方法 -> 案例如下:
  • const p1 = new Promise((resolve, reject) => {
        resolve('hello');
    .then(result => result)
    .catch(e => e);
    const p2 = new Promise((resolve, reject) => {
        throw new Error('报错了');
    .then(result => result)
    .catch(e => e);
    Promise.all([p1, p2])
        .then(result => console.log(result)) // 被执行了,输出了 p1 和 p2 的结果
        .catch(e => console.log(e));
    // ["hello", Error: 报错了]
    

    如上代码分析:p1 会 resolved, p2 首先会 rejected ,但是 p2 有自己的 catch 方法,该 catch 方法返回一个新的 Promise 实例,p2 实际上指向的是这个实例 -> 该实例 执行完 catch 之后,状态变成 resolved

    —> 所以导致 Promise.all() 方法的两个 promise 实例都会 resolved ->因此会调用 then 方法指定的回调

    考题:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现:

    async function A(){}
    async function B(){}
    function C(){}
    Promise.all([A(),B()]).then(C)
    
    const A = async () =>  await 'A';
    const B = async () =>  await 'B';
    const C = () =>  'C';
    (async function All() {
        await Promise.all([A(), B()]);
        C();
    })();
    

    Promise.race

    同样是将多个 Promise 包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);
      
  • 1、只要 p1、p2、p3 之中 有一个实例率先改变状态,p 的状态就跟着改变
  • 2、那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数
  • const p = Promise.race([
        fetch('/resource-that-may-take-a-while'),
        new Promise(function (resolve, reject) {
            setTimeout(() => reject(new Error('request timeout')), 5000)
    p.then(console.log)
     .catch(console.error);
    

    如上代码,如果 5秒 后 fetch 方法无法返回结果,变量 p 的状态会变为 rejected ,触发 catch 方法

    Promise.allSettled - ES2020

    作用:用于确定一组异步操作是否都结束了(不管成功 fulfilled 或失败 rejected

    const p = Promise.allSettled([p1, p2, p3]);
        

    1、只有等到参数数组的 所有 Promise 对象都发生状态变更(不管是 fulfilled 还是 rejected),返回的 Promise 对象才会发生状态变更

    2、该方法 返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected

    3、状态变成 fulfilled 后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的 每个 Promise 对象 - 对象的格式是固定的,如下

      // 异步操作成功时
      {status: 'fulfilled', value: value}
      // 异步操作失败时
      {status: 'rejected', reason: reason}
    
    const resolved = Promise.resolve(42);
    const rejected = Promise.reject(-1);
    const allSettledPromise = Promise.allSettled([resolved, rejected]); // 状态只可能变成 fulfilled
    allSettledPromise.then(function (results) {
        console.log(results);
    //    { status: 'fulfilled', value: 42 },
    //    { status: 'rejected', reason: -1 }
    
    const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
    const results = await Promise.allSettled(promises);
    // 过滤出成功的请求
    const successfulPromises = results.filter(p => p.status === 'fulfilled');
    // 过滤出失败的请求,并输出原因
    const errors = results
    .filter(p => p.status === 'rejected')
    .map(p => p.reason);
    

    Promise.any - ES2021

    定义:接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

  • 1、只要参数实例 有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态
  • 2、如果所有参数实例 都变成 rejected 状态,包装实例就会变成 rejected 状态
  • Promise.any([
        fetch('https://v8.dev/').then(() => 'home'),
        fetch('https://v8.dev/blog').then(() => 'blog'),
        fetch('https://v8.dev/docs').then(() => 'docs')
    ]).then((first) => {  // 只要有一个 fetch() 请求成功
        console.log(first);
    }).catch((error) => { // 所有三个 fetch() 全部请求失败
        console.log(error);