美文共赏

ES6 promise 用法小结

2021-12-13  本文已影响0人  小王子__

ES6 promise 用法小结

Js 是一⻔单线程语言,早期解决异步问题,大部分是通过回调函数进行。

比如我们发送 ajax 请求,就是常见的一个异步场景,发送请求后,一段时间服务器给我们响应,然后才拿到结果。如果我们希望在异步结束之后执行某个操作,就只能通过回调函数的方式进行操作

const request = function (callback) {
  setTimeout(function () {
    callback()
  }, 1000)
}
request(function () {
  console.log(123)
})
// 以上代码执行结果:1s 后输出 123
// request 就是一个异步函数,里面执行的 setTimeout 会在 1s 之后调用传入的 callback 函数, 
// 如果后续还有内容需要在异步函数结束时输出,就需要多个异步函数进行嵌套,非常不利于后续的维护。
setTimeout(function () {
  console.log(123)
  setTimeout(function () {
    console.log(321)
    // ...
  }, 2000)
}, 1000)

为了使回调函数以更优雅的方式进行调用,在 ES6 中引入了 promise,让异步 操作的变得「同步化」。

1,Promise 基础

通过 new Promise() 即可构造一个 promise 实例,这个构造函数接受一个函数,接受两个参数:resolvereject,代表改变实例的状态到 已完成 或是 已拒绝

const promise = new Promise(function (resolve, reject) {
  console.log('promise called')
  setTimeout(function () {
    resolve()
  }, 3000)
})

promise.then(function () {
  console.log('promise resolve callback')
})

// 先打印出 promise called, 3s 后打印 promise resolve callback
function promise1() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('1s后输出')
      resolve()
    }, 1000)
  })
}

function promise2() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('2s后输出')
      resolve()
    }, 2000)
  })
}
// 以下两个promise实例,串联起来即可写为:
promise1().then(function () {
  return promise2()
})
或
promise1().then(promise2)

控制台打印结果:1s之后出现 1s后输出,再经过2s出现2s后输出。实例中,当前promise如果状态变为已完成(执行resolve方法),就会去执行 then 方法中的下一个 promise 函数。同样的如果promise变成已拒绝状态(执行reject方法),就会进入后续的异常处理函数中。

function promise3() {
  return new Promise(function (resolve, reject) {
    var random = Math.random() * 10 // 随机一个 1 - 10的数字
    setTimeout(function () {
      if (random >= 5) {
        resolve(random) // 把随机生成的数字传给了 resolve, 在 then 中可以拿到这个值
      } else {
        reject(random)  // 把随机生成的数字传给了 reject,在 then 中可以拿到这个值
      }
    }, 1000)
  })
}

var onResolve = function (val) {  
  console.log('已完成:输出的数字是:', val)
}

var onReject = function (val) {
  console.log('已拒绝:输出的数字是:', val)
}

// promise 的then也可以接受两个参数,第一个参数是 resolve 后执行的,第二个参数是 reject 后执行的
promise3().then(onResolve, onReject)

// 也可以通过 .catch 方法拦截状态变为已拒绝时的 promise
promise3().catch(onReject).then(onResolve)

// 也可以通过 try catch 进行拦截状态变为已拒绝的 promise
try {
  promise3().then(onResolve)
} catch (e) {
  onReject(e)
}

以上使用3种方式拦截最终变为「已拒绝」状态的 promise,分别是使用 then 的第二个参数,使用 .catch 方法捕获前方 promise 抛出的异常,使用 try catch 拦截代码块中 promise 抛出的异常

我们可以发现,在改变 promise 状态时调用 resolvereject 函数的时候,可以给下一步 then 中执行的函数传递参数。

2,封装异步操作为promise

我们可以将任何接受回调的函数封装为一个promise, 实例:

// 原函数
function func(callback) {
  setTimeout(function () {
    console.log('1s 后显示')
    callback()
  }, 1000)
}

var callback = function () {
  console.log('在异步结束后打印')
}
// 用传入回调函数的方式执行
func(callback)
image
以上实例是最传统的使用传入回调函数的方式在异步结束后执行函数。我们可以通过封装 promise的方式,将这个异步函数变为 promise:
function func() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('1s 后显示')
      resolve()
    })
  })
}

var callback = function () {
  console.log('在异步结束后打印')
}

func().then(function () {
  callback()
})

再比如,我们发送 ajax 请求也可以封装为 promise:

function ajax(url, success, fail) {
  var client = new XMLHttpRequest();
  client.open('GET', url)
  client.onreadystatechange = function () {
    if (this.readyState !== 4) {
    // this.readyState扩展:
    // 0: 未初始化,还没调用 send() 方法
    // 1: 载入,已调用send()方法,正在发送请求
    // 2: 载入完成,send()执行完毕,已接受全部响应内容
    // 3: 交互,正在解析响应内容
    // 4: 完成,响应内容解析完成,可以直接使用responseText数据
      return
    }
    if (this.status === 200) {
      success(this.response)
    } else {
      fail(new Error(this.statusText))
    }
  }
  client.send()
}

ajax('http://localhost:8080/home/swiper', function (res) {
  console.log('成功')
  console.log(res)
}, function (err) {
  console.log('失败', err)
})
image
以上 ajax 请求,通过封装 promise 的方式,在原来的执行回调函数的地方,更改当前 promise的状态,就可以通过链式调用:
function ajax(url) {
  return new Promise(function (resolve, reject) {
    var ct = new XMLHttpRequest();
    ct.open('GET', url)
    ct.onreadystatechange = function () {
      if (this.readyState !== 4) {
        return
      }
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    ct.send()
  })
}

ajax('http://localhost:8080/home/swiper').catch(function () {
  console.log('失败')
}).then(function (res) {
  console.log('成功')
  console.log(res)
})

我们可以把任何一个函数或者是异步函数改为promise,尤其是异步函数,改为 promise中后即可进行链式调用,增强可读性

3,小总结

var promise1 = new Promise((resolve, reject) => {
  reject()
})
promise1
  .then(null, function () {
    return 123
  })
  .then(null, null)
  .then(null, null)
  .then(
    () => {
      console.log('promise2 已完成')
    },
    () => {
      console.log('promise2 已拒绝')
    })

以上代码输出:promise2 已完成

以上代码可改写为:

var promise1 = new Promise(function (resolve, reject) { reject() })
var promise2 = promise1.then(null, function () { return 123 })
var promise3 = promise2.then(null, null) // 如果 onFulfilled 不是函数且 promise2 状态变为已完成, promise3 必须成功执行并返回和 promise2 相同的值, 即 123
var promise4 = promise3.then(null, null) // 同理,promise4 也能拿到 123 的值
promise4
  .then(val => {
    console.log('promise2 已完成', val)  // promise2 已完成 123
  }, () => {
    console.log('promise2 已拒绝')
  })

实例:

var promise1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve()
    }, 1000)
  })
}

var promise2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve()
    }, 2000)
  })
}

promise1()
  .then(function () {
    return promise2()  // 此处返回一个 promise 实例
  })
  .then(function () {
    console.log('已完成')
  }, function () {
    console.log('已拒绝')
  })
image

4, promise 构造函数上的 静态方法

返回一个 promise 实例,并将它的状态设置为已完成,同时将他的结果作为传入 promise 实例的值

var promise = Promise.resolve(123)
promise.then(function (val) {
  console.log('已完成', val)
})
image

Promise.resolve 的参数也可以处理对象、函数等内容

返回一个 promise 实例,并将它的状态设置为已拒绝,同时也将他的结果作为原因传入 onRejected 函数

var promise = Promise.reject(123)
promise.then(null, function (val) {
  console.log('已拒绝', val)
})
image

返回一个 promise 实例,接受一个数组,里面含有多个 promise 实例,当所有 promise 实例都成 已完成 状态时,进入已完成状态,否则进入已拒绝状态。

var promise1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve()
    }, 1000)
  })
}

var promise2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve()
    }, 1000)
  })
}

Promise.all([promise1(), promise2()]).then(function () {
  console.log('全部 promise 均已完成')
})
image

以上代码为多个 promise 同时进行,等待 1s 打印 1 之后,再等待 1s 就 会打印 2 和全部 promise 均已完成。

var promise1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve(1)
    }, 1000)
  })
}

var promise2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve(2)
    }, 1000)
  })
}

Promise.race([promise1(), promise2()]).then(function (val) {
  console.log('有一个 promise 状态已经改变', val)
})
image

5, generator / async await

ES6 之后,我们可以使用 generator 和 async/await 来操作 promise,比起使用 promise 串行的调用来说,从语法层面 让调用关系 显得更加串行。

function promise1() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve()
    }, 1000)
  })
}

function promise2() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve()
    }, 1000)
  })
}

// 使用 generator 函数
function* gen() {
  yield promise1()
  yield promise2()
}
var g = gen()
g.next()
g.next() // 1 2

// 使用 async/await 函数
(async function () {
  try {
    await promise1()
    await promise2()
    console.log('已完成')
  } catch (e) {
    console.log(e)
    console.log('已拒绝')
  }
}())  // 1 2 已完成
上一篇下一篇

猜你喜欢

热点阅读