Promise 解决了哪些问题

  1. 多个异步请求并发的问题 Promise.all

  2. 解决异步请求中的回调地狱问题 async await 或者 then 链 或者 generate + co

    function ajax1() {
      return new Promise(resolve => {
        $.ajax({
          url: '/student',
          method: 'get',
          data: {
            class: 1
          },
          success: resolve
        });
      });
    }
    
    function ajax2(arr) {
      return new Promise(resolve => {
        $.ajax({
          url: '/score',
          method: 'get',
          data: {
            stuId: arr
          },
          success: resolve
        });
      });
    }
    
    function ajax3() {
      return new Promise(resolve => {
        $.ajax({
          url: '/jige',
          // ...
          success: resolve
        });
      });
    }
    
    // 第一种实现
    ajax1().then(result => {
      return ajax2(result.map(item => item.id));
    }).then(result => {
      return ajax3();
    }).then(result => {
      // ...
    });
    
    // 第二种实现
    async function handle() {
      let result = await ajax1();
      result = await ajax2(result.map(item => item.id));
      result = await ajax3();
      // 此处的 result 就是三次异步请求后获取的信息
    }
    handle();
    

Promise 规范

  1. Promise 是用来管理异步编程的,它本身不是异步的,new Promise 的时候会立即执行 executor 函数,只不过一般会在 executor 函数中处理一个异步操作;

  2. new Promise(executor(resolve, reject)=>{ })executor 函数的两个参数 resolve 回调reject 回调 的执行都是异步微任务;

Promise 状态

  1. Promise 本身有三个状态:

    1. pending:初始状态,等待态;
    2. fulfilled:操作成功完成 (resolve 执行,PromiseStatus 会从 pending -> fulfilled)
    3. rejected:操作失败 (reject 执行,PromiseStatus 会从 pending -> rejected)
  2. 执行 resolve/reject 函数,执行这两个函数中的一个,都可以修改 Promise[[PromiseStatus]][[PromiseValue]] ,一旦状态被改变,再执行 resolvereject 就没有用了;

    let p1 = new Promise((resolve, reject) => {
      setTimeout(_ => {
        // 一旦状态被改变,再次执行 resolve、reject 就没有用了
        resolve('ok');
        reject('no');
      }, 200);
    });
    
    setTimeout(()=>{
      console.log('s', p1); // Promise { 'ok' }
    }, 1000);
    

Promise 的链式调用

catch 方法

  1. .catch(fn) <== 等价 ==> .then(null, onRejected)

  2. 示例代码:

    JavaScript
    JavaScript
    Promise.resolve(10).then(result => {
      console(a);//=>报错了
    }).catch(reason => {
      console.log(`失败:${reason}`);
    });
    
    Promise.resolve(10).then(result => {
      console(a);//=>报错了
    }).then(null, reason => {
      console.log(`失败:${reason}`);
    });
    

then 方法

  1. Promise.prototype.then([resolvedFn], [rejectedFn])

    1. resolvedFn:成功执行的方法;
    2. rejectedFn:失败执行的方法;
  2. 示例代码:

    // new Promise 的时候先执行 executor 函数
    let p1 = new Promise((resolve, reject) => {
      setTimeout(_ => {
        let ran = Math.random();
        console.log(ran);
    
        if (ran < 0.5) {
          reject('NO!');
          return;
        }
    
        resolve('OK!');
      }, 1000);
    });
    
    // p1.then 基于 then 方法,存储起来两个函数
    // executor 中的异步操作结束,resolve/reject 控制 Promise 状态,决定执行 then 存储函数中的某一个
    p1.then(result => {
      // 成功:ok
      console.log(`成功:` + result);
    }, reason => {
      // 失败:no
      console.log(`失败:` + reason);
    });
    
  3. 为什么很多人说 promise 是异步的

    // resolve/reject 的执行,不论是否放到一个异步操作中,都需要等待 then 先执行完
    // 因为此时 then 还没有执行,没有存储 then 中的操作,resolve/reject 没办法执行 then 中对应的操作
    // 此处是一个异步操作(所以很多人说 promise 是异步的),而且是微任务操作
    let p1 = new Promise((resolve, reject) => {
      resolve(100);
    });
    
    p1.then(result => {
      console.log(`成功:` + result);
    }, reason => {
      console.log(`失败:` + reason);
    });
    console.log(3);
    // 输出:
    //   3
    //   成功:100
    

then 链机制

  1. 每次执行完 promsie.then 之后返回的都是新的 promise 实例 (不能返回 this ,因为状态一经修改就不允许改变了,then 链的状态值有可能会变化)

  2. 新任务的状态取决于后续处理:

    1. 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据;
    2. 若有后续处理但还未执行,新任务挂起;
    3. 若后续处理执行了,则根据后续处理的情况确定新任务的状态;
      1. 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值;
      2. 后续处理执行有错,新任务的状态为失败,数据为异常对象;
      3. 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致;
  3. then 链 练习

    JavaScript
    JavaScript
    JavaScript
    JavaScript
    Promise.resolve(10)
    .then(
      (result) => {
        console.log(`成功:${result}`);
        // 返回值是 promise,则根据 promise 的状态来执行 then 的 成功/失败 回调;
        return Promise.reject(result * 10);
      },
    )
    .then(
      (result) => console.log(`成功:${result}`),
      (reason) => console.log(`失败:${reason}`)
    );
    // 成功:10
    // 失败:100
    
    let p1 = new Promise((resolve, reject) => resolve(100));
    
    let p2 = p1.then(
      (result) => {
        console.log("成功:" + result);
        result + 100;
        // 没有写 return,执行下一次 then 的成功回调函数,并将 undefinded 传入;
      }
    );
    
    let p3 = p2.then(
      (result) => {
        console.log("成功:" + result);
      },
      (reason) => {
        console.log("失败:" + reason);
      }
    );
    // 成功:100
    // 成功:undefined
    
    new Promise((resolve, reject) => {
      // 如果是代码报错的情况,一定会执行下一次 then 的失败回调函数
      throw new Error("代码错误");
    })
      .then(
        (result) => {
          console.log(`成功:${result}`)
        },
        (reason) => {
          console.log(`失败:${reason}`)
        }
      );
    // 失败:Error: 代码错误
    
    Promise.reject(10)
    .then(result => {
      // 错误处理,如果距离自己最近的 then 没有错误处理,则向下查找
      console.log(`成功:${result}`);
      return result * 10;
    })
    .then(null, reason => console.log(`失败:${reason}`));
    // 失败:10
    

then 和 catch 的区别

  1. 如果在 then 的第一个函数里抛出了异常,后面的 catch 能捕获到,而当前 then 的第二个函数捕获不到;

  2. then 的第二个参数和 catch 捕获错误信息的时候会就近原则,如果是 promise 内部报错,reject 抛出错误后:

    JavaScript
    JavaScript
    /**
     * 示例一:then 的第二个参数和 catch 方法都存在的情况下,只有 then 的第二个参数能捕获到
     */
    Promise.resolve(10)
      .then((res) => {
        console(a); // 报错了
      })
      .then(
        (res) => {
          console.log(`成功:${res}`);
        },
        (reason) => {
          console.log(`失败1:${reason}`);
        }
      )
      .catch((reason) => {
        console.log(`失败2:${reason}`);
      });
    // 失败1:ReferenceError: a is not defined
    
    
    /**
     * 示例二:如果 then 的第二个参数不存在,则 catch 方法会捕获到
     */
    Promise.resolve(10)
      .then((result) => {
        console(a); // 报错了
      })
      .then((res) => {
        console.log(`失败1:${reason}`);
      })
      .catch((reason) => {
        console.log(`失败2:${reason}`);
      });
    
    // 失败2:ReferenceError: a is not defined
    

Promise 中的静态方法

方法名 含义
Promise.resolve(‘100’) 等价于 new Promise(resolve=> resolve(‘100’))
Promise.reject(‘error’) 等价于 new Promise((, reject) => reject(‘error’))
Promise.all([p1, p2]) · 只有 p1、p2 的状态都变成 fulfilledpromise 的状态才会变成 fulfilled ,此时 p1、p2 的返回值组成一个数组,
传递给 promise 的回调函数;
· 只要 p1、p2 之中有一个被 rejectedpromise 的状态就变成 rejected ,此时第一个被 reject 的实例的返回值,
会传递给 promise 的回调函数;
Promise.any([p1, p2]) 返回一个任务,任务数组任一成功则成功,任务全部失败则失败
Promise.race([p1, p2]) 只要 p1、p2 之中有一个实例率先改变状态,promise 的状态就跟着改变,值传递给 promise 的回调函数
Promise.allSettled([p1, p2]) 返回一个任务,任务数组全部已决则成功,该任务不会失败

案例

中断 Promise

const wrap = (promise) => {
  let abort;

  // 实现一个 promise,之后可控制该 promsie 来控制 race
  let myP = new Promise((resolve, reject) => {
    abort = reject;
  });

  let p = Promise.race([promise, myP]);
  p.abort = abort;

  return p;
};


// 测试
let promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve("ok 成功了"), 10000);
});

let p = wrap(promise);
p.then(
  (data) => console.log(data),
  (err) => console.log(err)
);

// 需求:2s 后中断请求
setTimeout(() => p.abort("promise 超时"), 2000);

中断 then 链

Promise.resolve(100)
  .then((data) => {
    return new Promise(() => {}); // 直接返回一个 new Promise,中断 then 链
  })
  .then(
    (data) => {
      console.log(data);
    },
    (err) => {
      console.log(err);
    }
  );

defer 的作用

  1. 作用:deferred 对象实现了 Promise 并且将 resolvereject 方法暴露在了构造函数外面,Promise 对象的状态更为灵活;状态的改变只有一次,之后的更改会忽略;

    let deferred = {};
    deferred.promise = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });
    
    setTimeout(() => deferred.resolve(1), 1000);
    deferred.promise.then((res) => console.log("ok:", res));
    // ok: 1
    
  2. 使用场景:

    JavaScript
    JavaScript
    JavaScript
    /**
     * 减少代码嵌套
     */
    let fs = require("fs");
    
    // 实现 promise 延迟对象 defer
    let Deferred = function () {
      let dfd = {};
      dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
      });
      return dfd;
    };
    
    // function read() {
    //     return new Promise((resolve, reject) => {
    //         fs.readFile('./a.txt', 'utf8', (err, data) => {
    //             if (!err) resolve(data)
    //         })
    //     })
    // }
    
    // 减少代码嵌套
    function read() {
      let defer = Deferred();
    
      fs.readFile("./a.txt", "utf8", (err, data) => {
        if (!err) defer.resolve(data);
      });
    
      return defer.promise;
    }
    
    read().then((data) => console.log(data));
    
    /**
     * 多个地方想控制 1 个 Promise 的状态,回调只想执行 1 次
     */
    let deferred = {};
    deferred.promise = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });
    
    // 异步操作的顺序不确定
    setTimeout(() => deferred.resolve(), 10 * Math.random());
    setTimeout(() => deferred.resolve(), 10 * Math.random());
    
    // 只要有 1 个异步操作完成就执行回调
    deferred.promise.then(() => console.log("ok"));
    
    /**
     * 多个异步之间的协作方案,多个延迟对象配合使用
     */
    let Deferred = function () {
      let dfd = {};
      dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
      });
      return dfd;
    };
    
    let d1 = Deferred();
    let d2 = Deferred();
    
    Promise.all([d1.promise, d2.promise]).then((res) => {
      console.log(res); // [ 'Fish', 'Pizza' ]
    });
    d1.resolve("Fish");
    d2.resolve("Pizza");
    

面试题

看程序写结果

JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
Promise.resolve(1).then((res) => {
    console.log(res)
    return 2
}).catch((err) => {
    return 3
}).then((res) => {
    console.log(res)
})

// 1
// 2
// 第一个 then 中没有异常,catch 捕获不到异常,则执行第二个 then
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    }, 1000)
})
const promise2 = promise1.then(() => {
    throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {
    console.log('promise1~', promise1)
    console.log('promise2~', promise2)
}, 2000)

// promise1 Promise {<pending>}
// promise2 Promise {<pending>}
// promise1~ Promise {<fulfilled>: 'success'}
// promise2~ Promise {<rejected>: Error: error!!!}

// promise1、promise2 都是微任务执行,此时等待返回结果,状态为 pending
// 2s 后再次输出 promise1、promise2,此时微任务执行完毕,状态更新
setTimeout(() => console.log(5), 0);
new Promise(resolve => {
    console.log(1);
    resolve(3);
    Promise.resolve().then(() => console.log(4))
}).then(num => {
    console.log(num)
});
console.log(2);

// 1
// 2
// 4
// 3
// 5

// promise 构造函数里面是同步代码,构造函数的 resolve/reject 是异步的
// Promise.resolve().then 也是异步,此行代码先于 resolve 回调
Promise.resolve()
    .then(() => {
        return new Error('error!!!')
    })
    .then((res) => {
        console.log('then: ', res)
    })
    .catch((err) => {
        console.log('catch: ', err)
    })

// then:  Error: error!!!

// return 一个 error 对象并不会抛出错误,所以不会被后续的 catch 捕获
// 改成下面这样会被捕获异常
// return Promise.reject(new Error(‘error!!!’))
// throw new Error(‘error!!!’)
Promise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)

// 1

// .then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传
// 链式调用的参数不是函数,会发生值透传,传入的非函数值会被忽略
const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });
}));

first().then((arg) => {
    console.log(arg);
});

console.log(4);

// 3
// 7
// 4
// 1
// 2
// 5
var p = new Promise((resolve, reject) => {
    reject(Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))

// The Fails!
// The Fails!
var p = new Promise((resolve, reject) => {
    // 创建并返回一个被拒绝(rejected)的 Promise 对象,在这个 Promise 对象被拒绝时,它将抛出一个包含错误信息的 Error 对象
    return Promise.reject(new Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))

// Error: The Fails!
//     at /Users/wushuai/Desktop/javascript/1.js:2:27
//     at new Promise (<anonymous>)
//     at Object.<anonymous> (/Users/wushuai/Desktop/javascript/1.js:1:9)
//     at Module._compile (node:internal/modules/cjs/loader:1254:14)
//     at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
//     at Module.load (node:internal/modules/cjs/loader:1117:32)
//     at Module._load (node:internal/modules/cjs/loader:958:12)
//     at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
//     at node:internal/main/run_main_module:23:47
async function async1() {
  console.log(1);
  const result = await async2();
  console.log(3);
}
async function async2() {
  console.log(2);
}
Promise.resolve().then(() => {
  console.log(4);
});
setTimeout(() => {
  console.log(5);
});
async1();
console.log(6);

// 1
// 2
// 6
// 4
// 3
// 5
Promise.resolve('Success!').then(() => {
    throw Error('Oh noes!')
}).catch(error => {
    console.log('actually, that worked');
    return 'actually, that worked'
}).then(data => {
    throw Error('The fails!')
}).catch(error => console.log(error.message))

// actually, that worked
// The fails!
new Promise((resolve, reject) => {
  resolve('Success!')
}).then(() => {
  throw Error('Oh noes!')
}).catch(error => {
  console.log('catch=>' + error)
  return "actually, that worked"
}).catch(error => console.log(error.message))

// catch=>Error: Oh noes!
var p = new Promise((resolve, reject) => {
  reject(Error('The Fails!'))
})
  .catch(error => console.log('catch=>' + error))
  .then(error => console.log('then=>' + error))

// catch=> Error: The Fails!
// then => undefined

封装一个延时微任务

JavaScript
JavaScript
// 通过 async 封装
function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

async function test() {
    var temple = await sleep(1000);
    console.log(1111);
    return temple;
}
test();
// 通过 generate 来实现
function* sleep(ms) {
    yield new Promise((resolve, reject) => setTimeout(resolve, ms));
}

sleep(500).next().value.then(function () { 
  console.log(2222) 
});
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. Promise 解决了哪些问题
  2. 2. Promise 规范
  3. 3. Promise 状态
  4. 4. Promise 的链式调用
    1. 4.1. catch 方法
    2. 4.2. then 方法
    3. 4.3. then 链机制
    4. 4.4. then 和 catch 的区别
  5. 5. Promise 中的静态方法
  6. 6. 案例
    1. 6.1. 中断 Promise
    2. 6.2. 中断 then 链
    3. 6.3. defer 的作用
  7. 7. 面试题
    1. 7.1. 看程序写结果
    2. 7.2. 封装一个延时微任务