「ES6系列」Promise全解析

Promise 出现的原因

曾几何时,我们为了获取异步调用的结果,不得不大量使用回调函数,我们看下面这个例子:

通过Jquery的ajax获取服务器数据


   
   
  1. let url1 = 'http://xxx.xxx.1';

  2. $.ajax({

  3. url:url1,

  4. error:function (error) {},

  5. success:function (data1) {

  6. console.log(data1);

  7. }

  8. });

当需要发送多个异步请求,且每个请求之间需要相互依赖时,我们只能以嵌套的方式来解决


   
   
  1. let url1 = 'http://xxx.xxx.1';

  2. let url2 = 'http://xxx.xxx.2';

  3. let url3 = 'http://xxx.xxx.3';

  4. $.ajax({

  5. url:url1,

  6. error:function (error) {},

  7. success:function (data1) {

  8. console.log(data1);

  9. $.ajax({

  10. url:url2,

  11. data:data1,

  12. error:function (error) {},

  13. success:function (data2) {

  14. console.log(data2);

  15. $.ajax({

  16. url:url3,

  17. data,

  18. error:function (error) {},

  19. success:function (data3) {

  20. console.log(data3);

  21. }

  22. });

  23. }

  24. });

  25. }

  26. });

在处理多个异步逻辑时,就需要多层的回调函数嵌套,也就是我们常说的回调地狱。

这种编码模式的主要问题:

  • 代码臃肿。

  • 可读性差。

  • 耦合度过高,可维护性差。

  • 代码复用性差。

  • 容易滋生 bug。

  • 只能在回调里处理异常。

而Promise的出现,就是为了解决回调函数所引出的各种问题

什么是Promise

Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。

  • 从语法上讲,promise 是一个对象,从它可以获取异步操作的消息

  • 从本意上讲,它是承诺,承诺它过一段时间会给你一个结果

代码书写比较

首先封装一个支持 Promise 的 ajax 方法,不理解这块代码的话,也没有关系,后文会逐步讲解 Promise 的执行机制


   
   
  1. function request(url,data = {}){

  2. return new Promise((resolve,reject)=>{

  3. $.ajax({

  4. url,

  5. data,

  6. success:function (data) {

  7. resolve(data);

  8. },

  9. error:function (error) {

  10. reject(error);

  11. }

  12. })

  13. });

  14. }

用 request 方法实现前面多个互相依赖的网络请求


   
   
  1. let url1 = 'http://xxx.xxx.1';

  2. let url2 = 'http://xxx.xxx.2';

  3. let url3 = 'http://xxx.xxx.3';

  4. request(url1)

  5. .then((data)=>{

  6. console.log(data);

  7. return request(url2,data)

  8. })

  9. .then((data)=>{

  10. console.log(data);

  11. return request(url3,data)

  12. })

  13. .then((data)=>{

  14. console.log(data)

  15. })

  16. .catch((error)=>{

  17. console.log(error);

  18. });

Promise 的特性

promise 的状态

  • pending (等待态)

  • fulfilled (完成态)

  • rejected (拒绝态)

终值与拒因

  • 终值:指的是 promise 被解决时传递给解决回调的值

  • 拒因:拒绝原因,指在 promise 被拒绝时传递给异常回调的拒绝原因

状态与状态关系,状态与终值和拒因的关系

  • pending 可以迁移至 fulfilled 或 rejected

  • fulfilled 不能迁移至其他状态,必须拥有一个不可变的终值

  • rejected 不能迁移至其他状态,必须拥有一个不可变的据因

Promise 的使用

构造函数

Promise 是一个构造函数,使用 new 操作符返回一个 promise 对象

构造函数接收一个 excutor 函数作为参数

excutor 函数有两个函数类型的参数 resolve 和 reject


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. // 在 excutor 函数中执行异步操作

  3. // 当异步操作完成后,调用 resolve 函数或 reject 函数

  4. });

  • 构造函数在调用时,excutor 函数会作为同步代码立即执行

  • 我们通常在 excutor 函数中执行我们的异步操作

  • 未调用 resolve、reject 函数时,promise 对象的状态为 pending


   
   
  1. let p1 = new Promise((resolve,reject)=>{

  2. setTimeout(()=>{

  3. console.log('p1');

  4. },1000);

  5. });

  6. // p1 的状态一直为 pending

  • 当调用 resolve 函数, resolve 的参数为非 promise 对象、非 thenable 对象

    • resolve 函数的参数,作为 promise 对象的终值

    • promise 对象的状态变为 fulfilled


   
   
  1. let p2 = new Promise((resolve,reject)=>{

  2. setTimeout(()=>{

  3. console.log('p2');

  4. resolve('我是p2的终值')

  5. },1000);

  6. });

  7. // 代码执行,1000ms内,p2 的状态为 pending

  8. // 代码执行,1000ms后,p2 的状态为 fulfilled

  9. // 代码执行,1000ms后,p2 的终值为 '我是p2的终值'

  • 当调用 resolve 函数, resolve 的参数为 promise 对象

    • promise 对象的状态、终值、拒因与传入的 promise 对象同步


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. reject('error')

  3. })

  4. let p1 = new Promise((resolve,reject)=>{

  5. resolve(p)

  6. })

  7. // p1 的状态为 rejected ,拒因为 error

  • 当调用 resolve 函数, resolve 的参数为 thenable 对象

    • 会对 thenable 对象进行展开操作,promise 对象的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果


   
   
  1. let thenable1 = {

  2. then:function(resolve,reject){

  3. resolve(1)

  4. }

  5. }

  6. let thenable2 = {

  7. then:function(resolve,reject){

  8. reject(2)

  9. }

  10. }

  11. let thenable3 = {

  12. then:function(resolve,reject){

  13. throw new Error(3)

  14. }

  15. }

  16. let thenable4 = {

  17. then:function(fn1,fn2){

  18. //不调用 fn1 fn2

  19. }

  20. }

  21. let p1 = new Promise((resolve,reject)=>{

  22. resolve(thenable1);

  23. })

  24. let p2 = new Promise((resolve,reject)=>{

  25. resolve(thenable2);

  26. })

  27. let p3 = new Promise((resolve,reject)=>{

  28. resolve(thenable3);

  29. })

  30. let p4 = new Promise((resolve,reject)=>{

  31. resolve(thenable4);

  32. })

  33. // p1 的状态为 fulfilled 终值为 1

  34. // p2 的状态为 rejected 终值为 2

  35. // p3 的状态为 rejected 拒因为 Error:3

  36. // p4 的状态为 pending

  • 当调用 reject 函数, reject 函数的参数,作为 promise 对象的拒因

  • promise 对象的状态变为 rejected


   
   
  1. let p3 = new Promise((resolve,reject)=>{

  2. setTimeout(()=>{

  3. console.log('p3');

  4. reject('我是p3的拒因')

  5. },1000);

  6. });

  7. // 代码执行,1000ms内,p3 的状态为 pending

  8. // 代码执行,1000ms后,p3 的状态为 rejected

  9. // 代码执行,1000ms后,p3 的拒因为 '我是p3的拒因'

promise对象上的方法

then方法:

promise 提供一个 then 方法,用于访问其终值和拒因。

promise 的 then 方法接受两个参数:


   
   
  1. promise.then(onFulfilled, onRejected);

  • onFulfilled 函数用于当 promise 状态变为 fulfilled 时,接收终值

  • onRejected 函数用于当 promise 状态变为 rejected 时,接收拒因


   
   
  1. new Promise((resolve,reject)=>{

  2. setTimeout(()=>{

  3. resolve('异步任务获取的数据')

  4. },50)

  5. }).then((data)=>{

  6. console.log(data)

  7. })

  8. // 异步任务获取的数据


   
   
  1. new Promise((resolve,reject)=>{

  2. setTimeout(()=>{

  3. reject(new Error('异步任务异常'))

  4. },50)

  5. }).then(null,(error)=>{

  6. console.log(error)

  7. })

  8. // Error: 异步任务异常


   
   
  1. new Promise((resolve,reject)=>{

  2. throw new Error('抛出一个异常');

  3. }).then(null,(error)=>{

  4. console.log(error)

  5. })

  6. // Error: 抛出一个异常

onFulfilled 和 onRejected 参数可选
  • 如果 onFulfilled 不是函数,其必须被忽略

  • 如果 onRejected 不是函数,其必须被忽略

onFulfilled 特性

如果 onFulfilled 是函数:

  • 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值

  • 在 promise 执行结束前其不可被调用

  • 其调用次数不可超过一次

onRejected 特性

如果 onRejected 是函数:

  • 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因

  • 在 promise 被拒绝执行前其不可被调用

  • 其调用次数不可超过一次

onFulfilled 和 onRejected 的调用时机
  • 当 promise 对象的状态变为 fulfilled 或 rejected 时调用

  • onFulfilled、onRejected 永远都是异步调用

  • onFulfilled、onRejected 在事件队列中作为微任务来处理


   
   
  1. console.log(1);

  2. setTimeout(function(){

  3. console.log(2)

  4. },0)

  5. new Promise((resolve,reject)=>{

  6. resolve(3);

  7. }).then((data)=>{

  8. console.log(data);

  9. })

  10. console.log(4)

  11. // print: 1 4 3 2

onFulfilled 和 onRejected 的调用要求
  • onFulfilled 和 onRejected 必须被作为函数调用

  • 非严格模式下,this 为全局对象

  • 严格模式下,this 为 undefined


   
   
  1. function fn1(){

  2. new Promise((resolve)=>{

  3. resolve();

  4. }).then(function(){

  5. console.log(this)

  6. })

  7. }

  8. function fn2(){

  9. "use strict";

  10. new Promise((resolve)=>{

  11. resolve();

  12. }).then(function(){

  13. console.log(this)

  14. })

  15. }

  16. fn1(); // print: window

  17. fn2(); // print: undefined

then方法的多次调用
  • then 方法可以被同一个 promise 对象多次调用

  • then 方法会返回一个新的 promise 对象

  • 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调

  • 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调


   
   
  1. let p = new Promise((resolve)=>{

  2. resolve()

  3. });

  4. let p1 = p.then(()=>{

  5. console.log('异步执行,第一个onFulfilled');

  6. });

  7. let p2 = p.then(()=>{

  8. console.log('异步执行,第二个onFulfilled');

  9. });

  10. console.log(p1.constructor === Promise);

  11. console.log(p === p1);

  12. console.log(p === p2);

  13. console.log(p1 === p2);

  14. // print: true

  15. // print: false

  16. // print: false

  17. // print: false

  18. // print: 异步执行,第一个onFulfilled

  19. // print: 异步执行,第二个onFulfilled

then方法的返回值

then 方法返回一个 promise 对象


   
   
  1. promise2 = promise1.then(onFulfilled, onRejected);

  • 若 onFulfilled 、onRejected 返回一个非promise对象、非thenable对象的值 x ,则 promise2 的状态为 fulfilled ,终值为 x


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. throw new Error();

  3. });

  4. let p1 = p.then(null,(data)=>{

  5. return '我是p2的终值'

  6. });

  7. p1.then((data)=>{

  8. console.log(data)

  9. });

  10. // print: 我是p2的终值

  • 若 onFulfilled 、onRejected 返回一个 promise 对象的值 x ,promise2 的状态、终值、拒因与 x 同步


   
   
  1. let p1 = new Promise((resolve,reject)=>{

  2. resolve(1)

  3. })

  4. let p2 = new Promise((resolve,reject)=>{

  5. reject(2)

  6. })

  7. let p3 = new Promise((resolve)=>{

  8. resolve()

  9. })

  10. let p4 = p3.then(()=>{

  11. return p1;

  12. })

  13. let p5 = p3.then(()=>{

  14. return p2;

  15. })

  16. // p4 的状态为 fulfilled 终值为 1

  17. // p5 的状态为 rejected 拒因为 2

  • 若 onFulfilled 、onRejected 返回一个 thenable 对象 ,会对 thenable 对象进行展开操作,promise2 的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果


   
   
  1. let thenable1 = {

  2. then:function(resolve,reject){

  3. resolve(1)

  4. }

  5. }

  6. let thenable2 = {

  7. then:function(resolve,reject){

  8. reject(2)

  9. }

  10. }

  11. let p1 = new Promise((resolve,reject)=>{

  12. resolve()

  13. })

  14. let p2 = p1.then(()=>{

  15. return thenable1;

  16. })

  17. let p3 = p1.then(()=>{

  18. return thenable2;

  19. })

  20. // p2 的状态为 fulfilled 终值为 1

  21. // p3 的状态为 rejected 拒因为 2

  • 若 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 的状态为 rejected,拒因为 e


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. resolve();

  3. });

  4. let p1 = p.then((data)=>{

  5. throw new Error('error')

  6. });

  7. p1.then(null,(err)=>{

  8. console.log(err);

  9. });

  10. // print: Error: error

  • 若 onFulfilled 不是函数且 promise1 成功执行, promise2 的状态为 fulfilled 终值为 promise1 的终值


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. resolve('我是p1的终值');

  3. });

  4. let p1 = p.then(null,null);

  5. p1.then((data)=>{

  6. console.log(data);

  7. });

  8. // print: 我是p1的终值

  • 若 onRejected 不是函数且 promise1 拒绝执行, promise2 的状态为 rejected 拒因为 promise1 的拒因


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. reject('我是p1的拒因');

  3. });

  4. let p1 = p.then(null,null);

  5. p1.then(null,(err)=>{

  6. console.log(err);

  7. });

  8. // print:我是p1的拒因

  • 若 onFulfilled、onRejected 执行过程中抛出异常,则 promise2 的状态为 rejected 拒因为抛出的异常


   
   
  1. let p = new Promise((resolve,reject)=>{

  2. resolve('我是p的终值');

  3. });

  4. let p1 = p.then((data)=>{

  5. throw new Error('异常')

  6. });

  7. p1.then(null,(err)=>{

  8. console.log(err);

  9. });

  10. // print:Error: 异常

终值和拒因的穿透特性
  • 如果 promise 的状态变为 fulfilled,then 方法没有注册 onFulfilled

    • then 方法返回的 promise 对象的状态变为 fulfilled

    • then 方法返回的 promise 对象的终值与原 promise 对象的终值相同

  • 如果 promise 的状态变为 rejected,then 方法没有注册 onRejected

    • then 方法返回的 promise 对象的状态变为 rejected

    • then 方法返回的 promise 对象的拒因与原 promise 对象的拒因相同


   
   
  1. let p1 = new Promise((resolve,reject)=>{

  2. resolve(1)

  3. })

  4. let p2 = new Promise((resolve,reject)=>{

  5. reject(2)

  6. })

  7. let p3 = p1.then(null,null);

  8. let p4 = p2.then(null,null);

  9. // p3 的状态是 fulfilled 终值 1

  10. // p4 的状态是 rejected 拒因 2

  11. p5 = p3.then(null,null);

  12. p6 = p4.then(null,null);

  13. // p3 的状态是 fulfilled 终值 1

  14. // p4 的状态是 rejected 拒因 2

  • 穿透特性主要用于异常处理


   
   
  1. let fn1 = function(){}

  2. let fn2 = function(){}

  3. let fn3 = function(){}

  4. let fn4 = function(){}

  5. let fn5 = function(){}

  6. let onError = function(){};

  7. new Promise((resolve,reject)=>{

  8. setTimeout(function(){

  9. reject()

  10. })

  11. })

  12. .then(fn1)

  13. .then(fn2)

  14. .then(fn3)

  15. .then(fn4)

  16. .then(fn5)

  17. .then(null,onError)

fn1、fn2、fn3、fn4、fn5 都可能发生错误,通过在最后的then函数注册的 onRejected 函数接收可能发生异常错误

catch方法:

catch(fn) 方法实际上是 then(null,fn) 方法的别名,catch 方法的返回值以及 catch 方法中出现异常的情况与调用 then 方法相同


   
   
  1. new Promise((resolve,reject)=>{

  2. reject()

  3. }).then(null,function(error){

  4. })

  5. // 等同于

  6. new Promise((resolve,reject)=>{

  7. reject()

  8. }).catch(function(error){

  9. })

Promise 的静态方法

Promise.resolve
  • Promise.resolve 方法用于将现有数据转换为 promise 对象

    • 返回的 promise 对象的状态为 fulfilled

    • 返回的 promise 对象的终值为 Promise.resolve 方法的入参

    • 会对 thenable 对象进行展开操作,返回的 promise 对象的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果

    • 返回的 promise 对象的状态、终值、拒因与 Promise.resolve 方法的入参同步

    • 若入参为 promise 对象

    • 若入参为 thenable 对象

    • 若入参为非 promise 非 thenable 对象


   
   
  1. let p = Promise.resolve(x)

  2. // 等价于

  3. let p = new Promise((resolve)=>{

  4. resolve(x)

  5. })

Promise.reject
  • Promise.reject 方法用于返回一个状态为 rejected ,拒因为方法入参的 promise 对象


   
   
  1. let p = Promise.reject(x)

  2. // 等价于

  3. let p = new Promise((resolve,reject)=>{

  4. reject(x)

  5. })

Promise.all
  • Promise.all 方法用于将多个 promise 对象包装成一个新的 promise 对象


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

  • p1、p2、p3 都是 promise 对象,如果不是,调用 Promise.resolve 方法转换为 promise 对象

  • p 的状态由 p1、p2、p3 决定

    • p 的状态变为 rejected

    • 此时第一个状态变为 rejected 的 promise 对象的拒因作为 p 的拒因

    • p 的状态为 fulfilled

    • 此时 p1、p2、p3 的终值组成一个数组,这个数组作为 p 的终值

    • 当 p1、p2、p3 的状态都变成 fulfilled

    • 当 p1、p2、p3 的状态有一个变成 rejected


   
   
  1. let p1 = Promise.resolve(1);

  2. let p2 = Promise.resolve(2);

  3. let p3 = 3;

  4. Promise.all([p1,p2,p3]).then((data)=>{

  5. console.log(data); // print: [1,2,3]

  6. })


   
   
  1. let p1 = Promise.resolve(1);

  2. let p2 = new Promise((resolve,reject)=>{

  3. setTimeout(function(){

  4. reject('p2 error')

  5. },1000)

  6. })

  7. let p3 = new Promise((resolve,reject)=>{

  8. setTimeout(function(){

  9. reject('p3 error')

  10. },500)

  11. })

  12. Promise.all([p1,p2,p3]).catch((error)=>{

  13. console.log(error); // print: p3 error

  14. })

Promise.race
  • Promise.all 方法同样用于将多个 promise 对象包装成一个新的 promise 对象


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

  • p1、p2、p3 都是 promise 对象,如果不是,调用 Promise.resolve 方法转换为 promise 对象

  • p 的状态由 p1、p2、p3 中状态最先变为 fulfilled 或 rejected 的 promise 对象决定

  • p 的终值或拒因由最先变更状态的 promise 对象所决定


   
   
  1. let p1 = Promise.resolve(1);

  2. let p2 = new Promise((resolve,reject)=>{

  3. setTimeout(function(){

  4. reject('p2 error')

  5. },1000)

  6. })

  7. let p3 = new Promise((resolve,reject)=>{

  8. setTimeout(function(){

  9. reject('p3 error')

  10. },500)

  11. })

  12. Promise.race([p1,p2,p3]).then(data=>{

  13. console.log(data);

  14. }).catch(error=>{

  15. console.log(error);

  16. })

  17. // print: 1


   
   
  1. let p1 = new Promise((resolve,reject)=>{

  2. setTimeout(function(){

  3. resolve(1)

  4. },1000)

  5. })

  6. let p2 = new Promise((resolve,reject)=>{

  7. setTimeout(function(){

  8. reject('p2 error')

  9. },800)

  10. })

  11. let p3 = new Promise((resolve,reject)=>{

  12. setTimeout(function(){

  13. reject('p3 error')

  14. },500)

  15. })

  16. Promise.race([p1,p2,p3]).then(data=>{

  17. console.log(data);

  18. }).catch(error=>{

  19. console.log(error);

  20. })

  21. // print: p3 error

Promise 的错误捕获

当 promise 的状态为 rejected 且为对 promise 对象使用 catch 方法,此时的异常信息会被 promise 对象吃掉 可以通过监听 unhandledRejection 事件,专门监听未捕获的reject错误


   
   
  1. // node 环境下

  2. process.on('unhandledRejection', error => {

  3. console.log('unhandledRejection', error);

  4. });

  5. // 浏览器下

  6. window.addEventListener('unhandledrejection',(e)=>{

  7. e.preventDefault();

  8. console.log(e);

  9. });

Promise 的问题

  • 无法取消Promise,若没有状态变更,也无法停止 promise 的等待

  • 不设定 then 或 catch 方法,构造函数(excutor函数)错误,无法捕获

  • 未完成状态时,无法得知是刚开始,还是即将完成

Promise 题目

题目一


   
   
  1. const promise = new Promise((resolve, reject) => {

  2. console.log(1)

  3. resolve()

  4. console.log(2)

  5. })

  6. promise.then(() => {

  7. console.log(3)

  8. })

  9. console.log(4)

结果:1 2 4 3

题目二


   
   
  1. const promise = new Promise((resolve, reject) => {

  2. resolve('success1')

  3. reject('error')

  4. resolve('success2')

  5. })

  6. promise

  7. .then((data) => {

  8. console.log(data)

  9. })

  10. .catch((err) => {

  11. console.log(err)

  12. })

结果:success1

题目三


   
   
  1. Promise.resolve(1)

  2. .then((data) => {

  3. console.log(data)

  4. return 2

  5. })

  6. .catch((err) => {

  7. return 3

  8. })

  9. .then((data) => {

  10. console.log(data)

  11. })

结果:1 2

题目四


   
   
  1. Promise.resolve(1)

  2. .then(2)

  3. .then(Promise.resolve(3))

  4. .then(console.log)

结果:1

题目五


   
   
  1. new Promise((resolve,reject)=>{

  2. console.log(3);

  3. let p = new Promise((resolve, reject)=>{

  4. console.log(7);

  5. setTimeout(()=>{

  6. console.log(5);

  7. resolve(6);

  8. },0)

  9. resolve(1);

  10. });

  11. resolve(2);

  12. p.then((arg)=>{

  13. console.log(arg);

  14. });

  15. }).then((arg)=>{

  16. console.log(arg);

  17. });

  18. console.log(4);

结果:3 7 4 1 2 5

写在最后

  • 如果你觉得这篇文章稍有收获,点个 再看,让更多的人也能看到这篇文章~

  • 关注我的Github:https://github.com/chenqf,查看更多精品原创文章~

欢迎关注微信公众号 【前端小黑屋】,每周1-3篇精品优质文章推送,助你走上进阶之旅

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值