Promise

175 阅读5分钟

Promise 对象是一个构造函数

实例化的过程是同步的,回调是异步的

    const promise = new Promise(function (resolve, reject) {
        // Promise 函数接受一个函数作为参数
        // 参数函数 有两个函数值 resolve, reject ,也是两个函数参数
        resolve() // 异步操作成功时的回调函数,并且将结果作为参数传递出去
        reject() // 异步操作失败时的回调函数,并且将结果作为参数传递出去
    })

Promise 函数特点

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。


Promise.prototype.then 方法

Promise 实例化之后,then方法指定了 resolve 和 reject状态的回调函数

    then(function (value) {
        // resolve 的回调
        // success
    },function (error) {
        // reject 的回调
        // error
    })
  • then方法在所有同步脚本执行完毕之后执行。
  • 由于then 方法返回的是一个新的Promise 实例
    then(resolve(), reject())
  • 因此可以采用链式写法,即then方法后面再调用另一个then方法

Promise.prototype.catch 方法

  • catch 方法是 then(null, rejection) 方法的别名

  • 当then()方法指定的回调函数运行中报错时, 也会被catch方法捕获。

    promise.then((val) => {
        console.log('resolve: ', val)
    }, (err) => {
        console.log('reject: ', err)
    })
    // 等同于
    promise.then((val) => {
        console.log('resolve: ', val)
    }).catch((err)=>{
        console.log('reject: ', err)
    })
    reject(new Error('test'))
    // 等同于
    throw new Error('test')
  • 如果resolve已完成,再抛出错误则无效(由promise函数的状态固定了就不变了的特点导致)
    const promise = new Promise(function (resolve, reject) {
        resolve('ok');
        throw new Error('test');
    });
    promise
        .then(function (value) { 
            console.log(value) 
        })
        .catch(function (error) { 
            console.log(error) 
        });
    // ok
  • Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
    getJSON('/post/1.json').then((post) => {
        return getJSON(post.commentURL)
    }).then((comments) => {
        ...
    }).catch(() => {
        // 处理上面3个promise 函数产生的错误(getJSON 和 两个then 方法)
    })
  • 建议用catch()方法代替 then()方法的第二个参数捕获错误信息

Promise 内部错误不会影响外部代码

  • promise 函数内部错误自己消化“Promise 会吃掉错误”,不会影响外部代码的执行
    const promise = new Promise(function (resolve, reject) {
		console.log(1) // 立即执行
		resolve(x + 1) // x为声明
		console.log(2) // 不执行
	})
	setTimeout(promise.then(() => {
		console.log('everything is ok')
	}), 3000)
	setTimeout(() => {
	    console.log(123)
	}, 1000)
	输出
	// 1
	// Uncaught (in promise) ReferenceError: x is not defined
	// 123
	
	上面的错误也可以在catch()中捕获
	setTimeout(promise.then(() => {
		console.log('everything is ok')
	}).catch((err) => {
	    console.log(err)
	}), 3000)
	// ReferenceError: x is not defined 

  • then() 直接报错

  • catch() 捕获错误

  • 如果 catch() 方法中不在控制台输出log日志,则不影响后面代码

  • 跟传统的try/catch代码块区别

    • 如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

unhandledRejection事件 专门监听未捕获的reject错误


Promise.prototype.finally()


Promise.prototype.all()

    const p = Promise.all([p1, p2, p3])
  • 只有p1、p2、p3的状态都变成fulfilled, p 的状态才会变成fulfilled
  • 只要p1、p2、p3 中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
    const promises = [2,3,5,7,11,13].map(function (id) {
        return getJSON('/post/' + id + '.json')
    })
    
    Promise.all(promises).then((posts) => {
        // ...
    }).catch((reason) => {
        // ...
    })
  • 如果作为参数的Promise实例,自己定义了catch方法,那么它一旦被rejected,并不会出发Promise.all()的catch方法。

Promise.race()

将多个Promise实例,包装成一个新的Promise实例


Promise.resolve()

将现有对象转为Promise对象

  • 参数是一个Promise实例

    如果参数是Promise实例,promise.resolve将原封不动的返回这个实例

  • 参数是一个thenable对象

    thenable对象是指具有then方法的对象,比如:

    let thenable = {
        then: fucntion (resolve, reject) {
            resolve(42)
        }
    }
    Promise.resolve(thenable).then((res) => {
	    console.log(res)
    })
	// 42
  • 参数不具有then方法,或者根本就不是对象

    Promise.resolve方法返回一个新的Promise对象,状态为resolved

  • 不带有任何参数

    Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的Promise对象。

    如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法

    立即resolve的Promise对象,是在本轮“事件循环”(event loop)结束时,而不是在下一轮“事件循环”的开始时。

   setTimeout(function () {
       console.log('three')
   }, 0)
   Promise.resolve().then(function () {
       console.log('two')
   })
   console.log('one')
   
   // one 
   // two
   // three
   // setTimeout(fn, 0)在下一轮“事件循环”开始时执行

Promise.reject()

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

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

应用

加载图片

    const preloadImage = function (path) {
        return new Promise((resolve, reject) => {
            const image = new Image()
            image.onload = resolve(path)
            image.onerror = reject(path)
            image.src = path
        })
    }