ES6-Promise详解

时间:2025-03-05 07:44:40

简介

  • javascript中,将函数作为对象使用时,被称为函数对象;new函数产生的对象,简称为对象; 而javascript中的回调函数同样分为了两种: (1)同步回调:立即执行,完全执行完了才会结束,不会放入回调队列中; (2) 异步回调:不会立即执行,会放入回调队列中来执行。 promise就是异步编程的一种解决方案,相对于最初的回调函数更加优雅。最早由社区提出,ES6将其写入了语言标准。

promise是什么

  • 通过阅读阮一峰老师的博客,可以看出,promise是一个容器,在这个容器中保存着异步操作的结果。语法上来看,promise是一个对象,可以获取异步操作的消息,promise提供了统一的api处理各种异步操作。

promise特点

  • 对象的状态不受外界影响。promsie对象有三种状态: pending(进行中), fulfilled(已成功)和rejected(已失败)。只有异步操作的结果可以决定当前的状态,不受任何其他因素的影响。
  • 一旦改变状态,就不会再变。 promise的状态改变,只有两种可能: pending => fulfilledpending => rejected.只有命中其中一种,那么状态就被凝固了。与事件不同的是: 状态改变了,再对promise对象添加回调函数也是会得到这个凝固的值。
  • 避免回调地狱(嵌套函数),代码更加优雅;利用promise可以实现链式调用,就可以将异步操作以同步操作的流程表达出,避免回调地狱的产生,并且promise对象也提供统一的接口,可以更加容易的操作异步操作。

promise的缺点

  • 无法取消promsie,因为promsie一旦new出来就是会立即执行的,无法中途取消
  • 不设置回调函数,无法捕获到promise内部抛出的错误
  • 处于pending阶段的时候,无法得知当前进行的阶段(刚刚开始还是即将完成)

promise基本用法

  • promise对象是一个构造函数,可以被new出来,生成promise实例。

    const p1 = new Promise(function(resolve, reject) {
    	// 注意: 这里都是同步的代码
    	if(success) {
    		// 成功的操作
    		resolve(value)
    	} else {
    		reject(error)
    	}
    })
    
  • 通过以上的代码可以看出构造函数接受函数为参数,注意该函数是同步函数,会被立即执行;其次,该函数的两个参数分别是resolve和reject,这是两个回调函数,由js引擎提供.

  • resolve主要是实现pending => resolved;如果异步操作成功,就会触发这个回调函数传递给外部使用。reject函数是将promise的状态从pending => ·rejected·,如果异步操作失败,那么该函数就会捕获到错误,传递给外部。

  • promise实例生成以后,可以用then方法分别指定resolvedrejected状态的回调函数,类似于下列代码所示

    p1.then(function(value) {
    	// 异步的代码
    	// 成功时,可以在这里做一些操作
    }, function(error) => {})
    
    function timeout(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, ms, 'done');
      });
    }
    
    timeout(100).then((value) => {
      console.log(value);
    });
    
  • timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数

  • ·promise一旦被实例后,就会被立即执行

    let promise = new Promise(function(resolve, reject) {
      console.log('Promise');
      resolve();
    });
    
    promise.then(function() {
      console.log('resolved.');
    });
    
    console.log('Hi!');
    
    // Promise
    // Hi!
    // resolved
    
  • 如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例,比如像下面这样。

    const p1 = new Promise(function (resolve, reject) {
      // ...
    });
    const p2 = new Promise(function (resolve, reject) {
      // ...
      resolve(p1);
    })
    
  • p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

  • 调用resolve或reject并不会终结 Promise 的参数函数的执行,因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行;但是一般来说,resolve或者reject以后,Promise的使命就完成了,后续操作应该放在then里面;所以在resovle之前应该加上return.

()

  • then返回的是新的promise实例,所以可以采用链式写法。一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

()

  • 用于指定发生错误时的回调函数。如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误
  • Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};
someAsyncThing().then(function() {
  console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123 // 123还是会被执行
  • Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误
  • 如果promise里还有事件的话,但是promise的运行已经结束的话,那么这个错误就会冒泡

()

  • finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

  • ()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  • ()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的方法,将参数转为 Promise 实例,再进一步处理。另外,()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
  • 会按顺序输出

()

  • ()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
  • 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
  • 和一样,如果传入的参数不是promise,会使用()将参数转为promise的实例。

()

  • ES2020引入, ()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。
  • 只有所有的参数实例都返回结果,包装实例才会结束
  • 该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入()的 Promise 实例。
  • 对于不关心操作有没有结束的功能时,就可以用这个。这个可以确保所有的操作都结束了。

()

  • ES2021引入,接受一组promise实例作为参数,包装成一个新的promise实例返回,只要有一个变成了fulfilled,那么包装实例就会变成fullfilled状态,如果所有参数都返回了rejected,那么包装实例就会变成rejected状态
  • 不会因为某个promise变成rejected状态而结束

()

  • 将现有对象转为 Promise 对象,()方法就起到这个作用。
  • 分为四种情况:
  • 参数是promise实例,那么不做任何修改,直接返回
  • 参数是thenable对象(具有then方法的对象),;会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
  • 参数不是具有then()方法的对象,或根本就不是对象,会通过这个方法转换成一个新的promise对象,状态时resolved;
  • 不带有任何参数:直接返回一个resolved状态的 Promise 对象。立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时

()

  • (reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

()

  • 函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法
  • 但是利用来包裹,那么就会在本轮事件循环的末尾执行;除非用async来书写。