Skip to content

promise

Promise 封装了一个异步操作并且可以获取成功或者失败的结果,有三种状态:pending 初始状态、fulfilled 成功状态、rejected 失败状态

状态改变只会有两种情况:pending=>fulfilled,pending=>rejected 一旦发生就不会再变

Promise 原理:构造一个 Promsie 实例,参数为一个函数,函数有两个形参,一个 resolve,一个 reject,执行 resolev 状态变为 fulfilled ,会执行.then 方法该参数是 resolve(res) 返回的结果;执行 reject 状态变为 rejected 会执行 catch 方法,参数为 reject(err) 返回的结果

Promise 链式调用:

js
const ppp = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('a')
  }, 1000)
})
  .then((res) => {
    console.log('res1', res)
    return new Promise((resolve) => resolve(res + 'a'))
  })
  .then((res) => {
    console.log('res', res)
    return new Promise((resolve) => resolve(res + 'a'))
  })
  .then((res) => {
    console.log('res3', res)
  })

简写可以省略 promise 不写,直接返回

js
const pppp = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('a')
  }, 1000)
})
  .then((res) => {
    return res + 'a'
  })
  .then((res) => {
    return res + 'a'
  })
  .then((res) => {
    console.log('res3', res)
  })

Promise.resolve()

有时需要将现有对象转为 Promise 对象,Promise.resolve 方法就起到这个作用

js
const jsPromise = Promise.resolve($.ajax('/whatever.json'))

Promise.resolve 等价于下面的写法

js
Promise.resolve('foo')
// 等价于
new Promise((resolve) => resolve('foo'))

Promise.resolve 的参数分为四种情况

参数是一个 Promise 实例

如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例

js
//如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。
function fn(resolve) {
  setTimeout(function () {
    return resolve(123)
  }, 3000)
}
let p0 = new Promise(fn)
let p1 = Promise.resolve(p0)
// 返回为true,返回的 Promise 即是 入参的 Promise 对象。
console.log(p0 === p1)

参数是一个 thenable 对象

thenable 对象指的是具有 then 方法的对象,比如下面这个对象

js
let thenable = {
  then: function (resolve, reject) {
    resolve(42)
  }
}

Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 then 方法

ES6 Promises 里提到了 Thenable 这个概念,简单来说它就是一个非常类似 Promise 的东西,最简单的例子就是 jQuery.ajax,它的返回值就是 thenable 对象,但是要谨记,并不是只要实现了 then 方法就一定能作为 Promise 对象来使用

js
let thenable = {
  then: function (resolve, reject) {
    resolve(42)
  }
}

let p1 = Promise.resolve(thenable)
p1.then(function (value) {
  console.log(value) // 42
})

上面代码中,thenable 对象的 then 方法执行后,对象 p1 的状态就变为 resolved,从而立即执行最后那个 then 方法指定的回调函数,输出 42

参数不是具有 then 方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved

JS
const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

不带有任何参数

Promise.resolve 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象,所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用 Promise.resolve 方法

js
const p = Promise.resolve()

p.then(function () {
  // ...
})

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

js
setTimeout(function () {
  console.log('three')
}, 0)

Promise.resolve().then(function () {
  console.log('two')
})

console.log('one')

// one
// two
// three

上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出

Promise.reject()

与 resolve 不同的是,返回的 promise 对象的状态为 rejected

js
const p = Promise.reject('出错了')
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
})
// 出错了
js
const thenable = {
  then(resolve, reject) {
    reject('出错了')
  }
}

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

Promise.race()

只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变,那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数

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

Promise.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回,只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态

Promise.any()跟 Promise.race()方法很像,只有一点不同,就是 Promise.any()不会因为某个 Promise 变成 rejected 状态而结束,必须等到所有参数 Promise 变成 rejected 状态才会结束

使用场景

将图片的加载写成一个 Promise,一旦加载完成,Promise 的状态就发生变化

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

通过链式操作,将多个渲染数据分别给个 then,让其各司其职。或当下个异步请求依赖上个请求结果的时候,我们也能够通过链式操作友好解决问题

js
// 各司其职
getInfo()
  .then((res) => {
    let { bannerList } = res
    //渲染轮播图
    console.log(bannerList)
    return res
  })
  .then((res) => {
    let { storeList } = res
    //渲染店铺列表
    console.log(storeList)
    return res
  })
  .then((res) => {
    let { categoryList } = res
    console.log(categoryList)
    //渲染分类列表
    return res
  })

通过 all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个 loading 即可

js
function initLoad() {
  // loading.show() //加载loading
  Promise.all([getBannerList(), getStoreList(), getCategoryList()])
    .then((res) => {
      console.log(res)
      loading.hide() //关闭loading
    })
    .catch((err) => {
      console.log(err)
      loading.hide() //关闭loading
    })
}
//数据初始化
initLoad()

通过 race 可以设置图片请求超时

js
//请求某个图片资源
function requestImg() {
  var p = new Promise(function (resolve, reject) {
    var img = new Image()
    img.onload = function () {
      resolve(img)
    }
    //img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正确的
    img.src = 'https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg1'
  })
  return p
}

//延时函数,用于给请求计时
function timeout() {
  var p = new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject('图片请求超时')
    }, 5000)
  })
  return p
}

Promise.race([requestImg(), timeout()])
  .then(function (results) {
    console.log(results)
  })
  .catch(function (reason) {
    console.log(reason)
  })