Promise的构造函数方法

Promise是什么:
1、认识Promise:
Promise 是异步操作的一种解决方案。
先给大家讲一下什么是异步:
回调函数其实就是异步操作,例:

      document.addEventListener(
        'click',
        () => {
          console.log('这里是异步的');
        },
        false
      );
      console.log('这里是同步的');

2、什么时候使用 Promise:
Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题。

在这里插入代码片

Promise的基本用法:
1、实例化构造函数生成实例对象:
Promise 解决的不是回调函数,而是回调地狱,即并不是用了Promise就用不了回调函数,它们两个是可以共存的。

// 实例化构造函数生成实例对象:
const p = new Promise(() => {});

2、Promise 的状态:
Promise的状态解决了Promise的一系列行为。Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功;执行 reject,变成 rejected,已失败;Promise 的状态一旦变化,就不会再改变了。

一开始是 pending(未完成):

      const p = new Promise((resolve, reject) => {

      });
      console.log(p); // Promise {<pending>

执行 resolve,变成 fulfilled(resolved),已成功:

      const p = new Promise((resolve, reject) => {
        resolve();
	  });
      console.log(p); // Promise {<fulfilled>: undefined}

执行 reject,变成 rejected,已失败:

      const p = new Promise((resolve, reject) => {
        reject();
      });
      console.log(p); // Promise {<rejected>: undefined}

但这里请大家注意:Promise里面第一个参数是成功状态的回调函数,第二个参数是失败状态的回调函数,Promise里面的resolve和reject只是形参(名字可以随便写,想起什么名就起什么名),写成resolve和reject的原因是为了语义化更强一些,那么,请问下面代码执行结果是什么:

          const p = new Promise((r1,r2) => {
            r2();
          });
          console.log(p);

因为r1 和 r2 只是形参,第二个参数是失败状态的回调函数,所以调用 r2 就等同于调用 失败状态的回调函数 所以毫无疑问结果是Promise {<rejected>: undefined},在这里只是给大家提个醒,大家不要弄迷糊了。

Promise 的状态一旦变化,就不会再改变了:

      const p = new Promise((resolve, reject) => {
        resolve();
        reject();
      });
      console.log(p); // Promise {<fulfilled>: undefined}

3、then方法(简单说明一下,具体在后面讲):
这里then传两个函数,成功了用第一个函数,失败了用第二个函数。

// 失败:
      const p = new Promise((resolve, reject) => {   
        reject();
      });

      p.then(
        () => {
          console.log('success');
        },
        () => {
          console.log('error');
        }
      ); 
      // error
// 成功:
      const p = new Promise((resolve, reject) => {   
        resolve();
      });

      p.then(
        () => {
          console.log('success');
        },
        () => {
          console.log('error');
        }
      ); 
      // success

4、resolve 和 reject 函数的参数:
resolve 和 reject 函数的参数都会传给相应的then方法里面函数的形参,即resolve()传它的参数给then里面第一个函数的形参,reject()传它的参数给then里面第二个函数的形参;举例:

// 成功的时候:
      const p = new Promise((resolve, reject) => {   
        resolve({username:'Alex'});
      });
      p.then(
        data => {
          console.log('success', data);
        },
        err => {
          console.log('error', err);
        }
      );
		// success {username: "alex"}
// 失败的时候:
      const p = new Promise((resolve, reject) => {   
        reject(new Error('reason'));
      });
      p.then(
        data => {
          console.log('success', data);
        },
        err => {
          console.log('error', err);
        }
      );
		// error Error: reason
    //		at 2-2.Promise 的基本用法.html:28
    //		at new Promise (<anonymous>)
    //		at 2-2.Promise 的基本用法.html:16

then方法:
1、什么时候执行:
pending->fulfilled 时,执行 then 的第一个回调函数
pending->rejected 时,执行 then 的第二个回调函数
2、执行后的返回值:
then 方法执行后返回一个新的 Promise 对象。

      const p = new Promise((resolve, reject) => {
        resolve();
        // reject();
      });
      const p2 = p
        .then(
          () => {},
          () => {}
        )
        .then() // 因为then方法返回的是一个promise,所以可以在后面再添加then方法。
        .then();
	  console.log(p, p2, p === p2); // Promise {<fulfilled>: undefined} Promise {<pending>} false

3、then 方法返回的 Promise 对象的状态改变:
在前面讲过第一个new 的promise可以决定紧挨着后面then的执行回调函数,例:

      const p = new Promise((resolve, reject) => {
        reject();
      });
      p.then(
        () => {
          console.log('success');
        },
        () => {
          console.log('err');
        });
        // err

因为在p中执行reject(),所以执行p紧挨着的then的第二个回调函数,因此输出err,
而我们都知道then方法可以继续使用在上一个then方法的后面的,但是大家有没有想过新的then方法是怎么执行的呢?下面进行讲解:
在讲解之前大家先判断一下下面这块代码的打印出来的值是多少?

// 案例1:
      const p = new Promise((resolve, reject) => {
        reject();
      });
      p.then(
        () => {
          console.log('success');
        },
        () => {
          console.log('err');
        }).then(
          data => {
            console.log('success2', data);
          },
          err => {
            console.log('err2', err);
          }
        )

这个值是err success2 undefined,
我先给大家分析一下,在p中执行reject(),所以在第一个then中执行第二个回调函数,所以打印出err,然后在第一个then中默认返回的永远都是成功状态的 Promise 对象,所以在第二个then中调用第第一个回调函数,所以接着打印出success2 undefined,由此可见后面的then执行第一个回调函数还是第二个回调函数看的是前面的then,可能大家对
在第一个then中默认返回的永远都是成功状态的 Promise 对象
这句话不太理解 ,下面我通过代码给大家演示:
先简单通过代码看一下:

//          在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
          return undefined;
//          等价于
          return new Promise(resolve => {
            resolve(undefined);
          });

这是详细讲解:
1、在所有函数中都有return值,没有写的时候就默认返回undefined,所以在第一个then里面就默认返回undefined(相当于程序中默认有 return undefined; 这样一行代码)

        () => {
          console.log('err');
          // 默认这里有 return undefined;这样一行代码
        })

又因为 默认返回的永远都是成功状态的 Promise 对象,即:

        () => {
          console.log('err');
          // 默认这里有 return undefined;这样一行代码
 // 因为默认返回的永远都是成功状态的 Promise 对象,所以相当于一个新的Promise里面执行resolve(),即:
          return new Promise((resolve, reject) => {
            resolve(undefined); // 因为默认返回undefined,所以resolve里面参数是undefined
        })

因为上一个then执行resolve,所以到第二个then那里就执行第一个回调函数,所以打印出 success2 undefined,打印出undefined是因为在一个then里面的reject传进去的undefined。讲到这里大家可能会问:默认返回的永远都是成功状态的 Promise 对象,那么如何返回失败状态的Promise对象呢?其实是这样的,then里面默认的是返回的永远都是成功状态的 Promise 对象,那么我直接给一个失败状态的Promise,那么不就不会再出现默认值了呢,没错,就是这样。举例:

      const p = new Promise((resolve, reject) => {
        reject();
      });
      p.then(
        () => {
          console.log('success');
        },
        () => {
          console.log('err');
/          
          return new Promise((resolve, reject) => {
            reject('reason');
           });
/          
        }).then(
          data => {
            console.log('success2', data);
          },
          err => {
            console.log('err2', err);
          }
        )

大家注意横线里面的两行代码,那样写即程序不会再执行那个 默认的正确状态的Promis对象了,而执行自己写的那个错误的Promise对象(即/中间的代码),所以,这个案例的执行结果是:err err2 reason,原理同案例1一样。

那么简单测试一下上面知识是否掌握了,上案例:

      const p = new Promise((resolve, reject) => {
        reject();
      });
      p.then(
        () => {
          console.log('success');
        },
        () => {
// 步骤一:
          console.log('err');
        }).then(
          data => {
// 步骤二:
            console.log('success2', data);
          },
          err => {
            console.log('err2', err);
          }
        ).then(
          data => {
// 步骤三:
            console.log('success3', data);
          },
          err => {
            console.log('err3', err);
          }
        );

只要大家将上面知识理解了,那么这个案例就是小菜一碟,答案是:err success2 undefined success3 undefined,执行的分别是步骤一,步骤二,步骤三。
catch方法:
1、有什么用:
在前面的then方法中,我们知道它里面有两个回调函数(第一个处理成功的状态,第二个处理失败的状态),但在实际开发中一般then只处理成功的状态,因此只传一个回调函数,因此catch就是用来专门处理失败的状态的,即catch 本质上是 then 的特例,就是then(null, err => {});
2、基本用法:

      new Promise((resolve, reject) => {
        // resolve(123);
        reject('reason');
      }).then(data => {
        console.log(data); // then 处理正确的状态
      }).catch(err => {
        console.log(err); // catch 处理错误的状态
      })
      // reason

// 上面的catch其实就相当于
        .then(null, err => {
          console.log(err);
        });

catch() 可以捕获它前面的错误,一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误:
catch可以捕获前面的错误,如果错误一旦被catc捕获到则错误就消失了。还有就是catch默认也是返回一个成功状态的Promise对象(原因很简单,catch本质是then,因为then默认返回成功状态的Promise对象,所以catch也是返回成功状态的Promise对象),所以catch后面跟的then只会执行成功的回调函数,如果想执行失败状态的回调函数需要自己手动return 失败状态的Promise对象,除了这种方法外还有一种方法,见代码:

      new Promise((resolve, reject) => {
        // resolve(123);
        reject('reason');
      })
        .then(data => {
          console.log(data);
        }) /// 第一块
        .catch(err => {
          console.log(err);
          throw new Error('reason');
        }) 
        .then(data => {
          console.log(data);
        })  第二块
        .catch(err => {
          console.log(err);
        }); /
		// reason
		// Error: reason

reason是第一块代码执行的结果, Error: reason是第二块代码执行的结果。在第一块代码里扔一个错误,那么就会被第二块代码中的catch捕获到,这样也不会再执行then里面的成功状态的回调函数了。
Promise.resolve() 和 Promise.reject():
1、Promise.resolve():
是成功状态 Promise 的一种简写形式。

      new Promise(resolve => resolve('foo'));
      // 这个地方的resolve是一个形参,名字想怎么写就怎么写
      // 简写
      Promise.resolve('foo'); 
      // 这里的resolve是一个固定的方法名,不能乱写

参数:
(1)一般参数(最应该关注):

      Promise.resolve('foo').then(data => {
        console.log(data);
      });  // foo

(2)特殊参数(简单了解一下即可,不要深究):
1、把Promise当作参数传进去。

        const p1 = new Promise(resolve => {
        setTimeout(resolve, 1000, '我执行了');
      });
      Promise.resolve(p1).then(data => {
        console.log(data);
      }); // 我执行了(注:在一秒后打印)

这是为什么呢?原因是:当 Promise.resolve() 接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做。
即上面的代码等同于:

      const p1 = new Promise(resolve => {
        setTimeout(resolve, 1000, '我执行了');
// 上面的setTimeout等同于:
        // setTimeout(() => {
        //   resolve('我执行了');
        // }, 1000);
      });
      p1.then(data => {
        console.log(data);
      }); // 我执行了(注:一秒后打印)
      console.log(Promise.resolve(p1) === p1); // true

还有就是,当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调。
上代码进行描述:

      const p1 = new Promise(resolve => {
        setTimeout(resolve, 1000, '我执行了');
      });
      new Promise(resolve => resolve(p1)).then(data => {
        console.log(data);
      });

上面那句话的意思就是如果函数接收的是一个Promise对象(即上面代码的p1)时,后面的then受p1支配,因为p1在一秒后执行了resolve('我执行了');所以就给then里面传了个 ‘我执行了’ ,所以then执行后打印出 我执行了。

2、具有 then 方法的对象:
因这种形式在实际开发中不咋用,这里不再细讲,大家知道有这个东西就行。(要是碰到了直接查文档就完事了)

2、Promise.reject():
失败状态 Promise 的一种简写形式。

      new Promise((resolve, reject) => {
        reject('reason');
      });
      // 等价于
      Promise.reject('reason');

参数:
不管什么参数,都会原封不动地向后传递,作为后续方法的参数。(跟resolve相比简单多了)

还有一个Promise.resolve() 和 Promise.reject()的用处就是用在then方法的return中,如:

      new Promise((resolve, rejcet) => {
        resolve(123);
      })
        .then(data => {
          // return data;
          // return Promise.resolve(data); // 传失败状态的Promise对象的简写
          return Promise.reject('reason'); // 传失败状态的Promise对象的简写
        })
        .then(data => {
          console.log(data);
        })
        .catch(err => console.log(err));
        // reason

Promise.all():
1、有什么用:
Promise.all() 关注多个 Promise 对象的状态变化;可以在Promise对象中传入多个 Promise 实例,包装成一个新的 Promise 实例返回。
2、基本用法:
Promise.all() 的状态变化与所有传入的 Promise 实例对象状态有关
所有状态都变成 resolved,最终的状态才会变成 resolved
只要有一个变成 rejected,最终的状态就变成 rejected
见代码:

// 封装一个延迟函数:
      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };

      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return 'p1'; // 默认返回成功状态的Promise对象
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return 'p2'; // 默认返回成功状态的Promise对象
      });
      
      const p = Promise.all([p1, p2]);
      
      p.then(
        data => {
          console.log(data);
        },
        err => {
          console.log(err);
        }
      );

上面代码中的执行结果是:p1完成了(一秒后),p2完成了 (2) [“p1”, “p2”] (两秒后)
这块代码是最终的状态是 resolved
再看下面的代码:

      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };

      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return Promise.reject('reason');
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return 'p2';
      });
      
      const p = Promise.all([p1, p2]);
      
      p.then(
        data => {
          console.log(data);
        },
        err => {
          console.log(err);
        }
      );

这块代码的结果是:p1完成了 reason (注:一秒后打印) p2完成了 (注:两秒后打印)
这块代码中有rejected,所以最终状态就是rejected,可能大家疑惑为什么reason在 p1完成了 后面出现,大家可以这样理解:因为系统检测到某个地方出现了rejected,所以直接判定Promise.all的状态是rejected,所以直接执行p后面then方法的第二个回调函数,所以打印出reason,又因为那个rejected的状态是在p1中检测到的,所以reason出现在 p1完成了 的后面。
大家可以通过下面的例子检测字迹是否理解:

      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };

      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return 'p1';
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return Promise.reject('reason');
      });
      
      const p = Promise.all([p1, p2]);
      
      p.then(
        data => {
          console.log(data);
        },
        err => {
          console.log(err);
        }
      );

这里的结果是:p1完成了(注:一秒后打印出来) p2完成了 reason(注:两秒后打印出来)
Promise.race() 和 Promise.allSettled():
先声明一下,这两个方法大家简单了解一下即可。
1、Promise.race():
Promise.race() 的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败。

      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };
      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return 'p1';
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return 'p2';
      });
      const racePromise = Promise.race([p1, p2]);
      racePromise.then(
        data => {
          console.log(data);
        },
        err => {
          console.log(err);
        }
      );
      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };
      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return 'p1';
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return Promise.reject('reason');
      });
      const racePromise = Promise.race([p1, p2]);
      racePromise.then(
        data => {
          console.log(data);
        },
        err => {
          console.log(err);
        }
      );

这里提供两个例子,供大家自己自行下去进行验证学习,这里就不细讲了,大家在自己编译器上一运行就一目了然了。
2、Promise.allSettled():
Promise.allSettled() 的状态与传入的Promise 状态无关,它后面的then方法永远执行成功的回调函数,它只会忠实的记录下各个 Promise 的表现。见代码:

      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };
      
      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return 'p1';
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return Promise.reject('reason');
      });
      
      const allSettledPromise = Promise.allSettled([p1, p2]);
      allSettledPromise.then(data => {
        console.log('succ', data);
      });
      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };
      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return Promise.reject('reason');
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return Promise.reject('reason');
      });
      const allSettledPromise = Promise.allSettled([p1, p2]);
      allSettledPromise.then(data => {
        console.log('succ', data);
      });

上面两个例子大家自行演示,看到结果大家都会明白的。
Promise的注意事项:
1、resolve 或 reject 函数执行后的代码:
推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码。
如:

      new Promise((resolve, reject) => {
      //   // return resolve(123);
        return reject('reason');
      });

虽然 resolve 或 reject 函数的后面的代码仍可以执行。如:

      new Promise((resolve, reject) => {
      //   // return resolve(123);
        return reject('reason');
        console.log('hi');
      }); // hi

但是不建议这样写。
2、Promise.all/race/allSettled 的参数问题:
参数如果不是 Promise对象 的数组,系统会默认的将不是 Promise 的数组元素转变成 Promise对象 的数组。

      Promise.all([1, 2, 3]).then(datas => {
        console.log(datas);
      });
      // 等价于
      Promise.all([
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3)
      ]).then(datas => {
        console.log(datas);
      });

参数不只是数组,任何可遍历的都可以作为参数:
如:原生可遍历的:数组、字符串、Set、Map、NodeList、arguments
和非原生可遍历的(通过Iterate)
举个栗子:

// Set作为参数
      Promise.all(new Set([1, 2, 3])).then(datas => {
        console.log(datas);
      });

3、Promise.all/race/allSettled 的错误处理:
单独处理:

// 第一个案例:
      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };

      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return Promise.reject('reason');
      }).catch(err => {
        console.log('p1', err);
      });
      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return Promise.reject('reason');
      }).catch(err => {
         console.log('p2', err);
      });

      const allPromise = Promise.all([p1, p2]);
      allPromise.then(datas => {
          console.log(datas);
        });

统一处理:

// 第二个案例:
      const delay = ms => {
        return new Promise(resolve => {
          setTimeout(resolve, ms);
        });
      };

      const p1 = delay(1000).then(() => {
        console.log('p1 完成了');
        return Promise.reject('reason');
      });

      const p2 = delay(2000).then(() => {
        console.log('p2 完成了');
        return Promise.reject('reason');
      });

      const allPromise = Promise.all([p1, p2]);
      allPromise.then(datas => {
          console.log(datas);
        }).catch(err => console.log(err));
  错误既可以单独处理,也可以统一处理
  一旦被处理,就不会在其他地方再处理一遍(上面第二个案例)
上一篇:简单版手写promiss,包含resolve,reject,then链式调用


下一篇:11.13知识整理