简谈Promise

300 阅读3分钟

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

场景说明

图片依次的加载

function(){
    var img1 = document.createElement('img')
	img1.onload = function () {
	    var img2 = document.createElement('img')
	    img2.onload = function(){
	        var img3 = document.createElement('img')
	        img3.onload = function(){
	            // ....
	        }
	    }
    }
}

这就是所谓的回调地狱,很显然,逻辑稍微复杂一些项目就很难维护了

Promise 的三种状态

  • pending-进行中,不会触发then 和 catch
  • resolved-已完成,会触发后续的 then 回调函数
  • rejected-已失败,会触发后续的 catch 回调函数
状态变化是不可逆的 (fulfilled不可能变为pending, rejected不可能变为pending状态)

then和catch改变状态

  • then 正常返回resolved,里面有报错则会返回rejected
const p1 = Promise.resolve().then(() => {
  return 100
})
console.log('p1', p1)
// resolve 触发后续的 then 回调
p1.then(() => {
  console.log(123)
})
const p2 = Promise.resolve().then(() => {
  throw new Error('then error')
})
// rejected 触发后续 catch 回调
console.log('p2', p2)
p1.then(() => {
  console.log('456')
}).catch(err => {
  console.error('error100', err)
})
  • catch 正常返回resolved,里面有报错则会返回rejected
const p3 = Promise.reject('my error').catch(err => {
  console.log(err)
})
console.log('p3', p3) // reject 里没有报错,状态为resolved 触发后续 then
p3.then(() => {
  console.log(100)
})
const p4 = Promise.reject('my error').catch(err => {
  throw new Error('catch error')
})
console.log('p3', p4) // 状态为rejected 触发后续 catch

p4.then(() => {
  console.log(200)
}).catch(() => {
  console.log('somef error')
})

Promise的基本用法

loadImg(src) => {
  // new Promise实例,接受两个参数(resolve,reject)
  const promise = new Promise((resolve, reject) => {
    var img = document.createElement('img')
    img.onload = () => {
	  resolve(img)
	}
	img.onerror = () => {
	  reject('图片加载失败')
	}
    img.src = src
  })
  return promise
}
var src = 'https://www.baidu.com/img/baidu_jgylogo3.gif'
var result = loadImg(src)
result.then((img) => {
  console.log('图片的宽度' + img.width)
}, () => {
  console.log('failed')
})
// 运行结果图片的宽度117

Promise多个串联

loadImg(src) => {
  // new Promise实例,接受两个参数(resolve,reject)
  const promise = new Promise((resolve, reject) => {
    var img = document.createElement('img')
    img.onload = () => {
	  resolve(img)
	}
	img.onerror = () => {
	  reject('图片加载失败')
	}
    img.src = src
  })
  return promise
}
// 多个串联
const src1 = 'https://www.baidu.com/img/baidu_jgylogo3.gif'
const result1 = loadImg(src1)
const src2 = 'https://ss0.baidu.com/73F1bjeh1BF3odCf/it/u=559043697,829837996&fm=85&s=816240A611420EFE36F52D900300E082'
const result2 = loadImg(src2)
result1.then((img1) => {
  console.log('第一张图片')
  return result2 
}).then((img2) => {
  console.log('第二张图片')
}).catch((ex) => {
  // 统一处理异常
  console.log(ex)
})

Promise.all() && Promise.race()

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例:

loadImg(src) => {
  // new Promise实例,接受两个参数(resolve,reject)
  const promise = new Promise((resolve, reject) => {
    var img = document.createElement('img')
    img.onload = () => {
	  resolve(img)
	}
	img.onerror = () => {
	  reject('图片加载失败')
	}
    img.src = src
  })
  return promise
}
const src1 = 'https://www.baidu.com/img/baidu_jgylogo3.gif'
const result1 = loadImg(src1)
const src2 = 'https://ss0.baidu.com/73F1bjeh1BF3odCf/it/u=559043697,829837996&fm=85&s=816240A611420EFE36F52D900300E082'
const result2 = loadImg(src2)
// Promise.all 接收一个 promise 对象的数组
// 待全部完成之后,统一执行success
Promise.all([result1, result2]).then((datas) => {
  // 接收到的datas 是一个数组,依次包含了多个promise返回的内容
  console.log('all', datas[0])
  console.log('all', datas[1])
})
// Promise.race 接受一个包含多个 promise对象的数组
// 只要有一个完成,就执行 success
Promise.race([result1, result2]).then((data) => {
  // 接收到的datas 是一个数组,依次包含了多个promise返回的内容
  console.log('race', data)
})

Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。

Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,