ES6 之 Promise

作者: passMaker | 来源:发表于2018-09-03 11:44 被阅读9次

Promise是JavaScript异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一

几种常见异步编程方案

参考
setTimeout 异步与回调 函数节流
阮一峰 - 异步操作概述

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象

回调函数 / 回调地狱

1秒钟之后输出 fn1, 再过1秒输出 fn2, 再过1秒输出 fn3。
即当我们需要发送多个异步请求,并且每个请求之间需要相互依赖时。这时 我们以嵌套方式来解决。形成了所谓的 "回调地狱"。

function fn1(callback) {
  setTimeout(()=>{
    console.log('fn1')
    callback()
  }, 1000)
}

function fn2(callback) {
  setTimeout(()=>{
    console.log('fn2')
    callback()
  }, 1000)
}

function fn3() {
  setTimeout(()=>{
    console.log('fn3')
  }, 1000)
}



fn1(function(){
  fn2(function(){
    fn3()
  })
})

这种编码方式有以下几个问题

  • 代码逻辑书写顺序与执行顺序不一致,不利于阅读与维护。
  • 异步操作的顺序变更时,需要大规模的代码重构。
  • 回调函数基本都是匿名函数,bug 追踪困难。

Promise 的使用

我们使用 Promise 可以以直观的方式,来解决 "回调地狱"。
即 Promise 处理多个相互关联的异步请求。

const promise = new Promise((resolve, reject) => {
       // 异步处理
       // 处理结束后、调用 resolve 或 reject
       // 如果成功就调用 resolve
       // 如果失败就调用 reject
})

promise.then(successFn, errorFn)

Promise 是一个对象,对象里存储一个状态,这个状态是可以随着内部的执行转化的,为以下三种状态之一:

  • 等待态(Pending)
  • 完成态(Fulfilled)
  • 拒绝态(Rejected)。

一开始,我们先设置好等状态从 pending 变成 fulfilled 和 rejected 的预案(当成功后我们做什么,失败时我们做什么)。

Promise 启动之后,当满足 成功 的条件时我们让状态从 pending 变成 fullfilled (执行 resolve);当满足 失败 的条件,我们让状态从 pending 变成 rejected(执行 reject

使用 Promise 解决刚才的回调函数

function fn1() {
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      console.log('fn1...')
      resolve()
    }, 1000)    
  })
}

function fn2() {
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      console.log('fn2...')
      resolve()
    }, 1000)    
  })
}

function fn3() {
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      console.log('fn3...')
      resolve()
    }, 1000)    
  })
}

function onerror() {
  console.log('error')
}

fn1().then(fn2).then(fn3).catch(onerror)

Promise 范例

Promise.prototype.then / Promise.prototype.catch

function getIp() {
  var promise = new Promise(function(resolve, reject){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getIp', true)
    xhr.onload = function(){
      var retJson = JSON.parse(xhr.responseText)  // {"ip":"58.100.211.137"}
      resolve(retJson.ip)
    }
    xhr.onerror = function(){
      reject('获取IP失败')
    }
    xhr.send()
  })
  return promise
}

function getCityFromIp(ip) {
  var promise = new Promise(function(resolve, reject){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
    xhr.onload = function(){
      var retJson = JSON.parse(xhr.responseText)  // {"city": "hangzhou","ip": "23.45.12.34"}
      resolve(retJson.city)
    }
    xhr.onerror = function(){
      reject('获取city失败')
    }
    xhr.send()
  })
  return promise
}
function getWeatherFromCity(city) {
  var promise = new Promise(function(resolve, reject){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getWeatherFromCity?city='+city, true)
    xhr.onload = function(){
      var retJson = JSON.parse(xhr.responseText)   //{"weather": "晴天","city": "beijing"}
      resolve(retJson)
    }
    xhr.onerror = function(){
      reject('获取天气失败')
    }
    xhr.send()
  })
  return promise
}

getIp().then(function(ip){
  return getCityFromIp(ip)
}).then(function(city){
  return getWeatherFromCity(city)
}).then(function(data){
  console.log(data)
}).catch(function(e){
  console.log('出现了错误', e)
})

promise.catch

在链式写法中可以捕获前面then中发送的异常。

promise.catch(onRejected)
//相当于
promise.then(null, onRrejected)  

// 注意
// onRejected 不能捕获当前onFulfilled中的异常
promise.then(onFulfilled, onRrejected)

// 可以写成:
promise.then(onFulfilled)
       .catch(onRrejected)

Promise.all

只有全部为 resolve 才会调用。通常会用来处理多个并行异步操作。

function getCityFromIp(ip) {
  var promise = new Promise(function(resolve, reject){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
    xhr.onload = function(){
      var retJson = JSON.parse(xhr.responseText)  // {"city": "hangzhou","ip": "23.45.12.34"}
      resolve(retJson)
    }
    xhr.onerror = function(){
      reject('获取city失败')
    }
    xhr.send()
  })
  return promise
}

var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')

//Promise.all, 当所有的 Promise 对象都完成后再执行
Promise.all([p1, p2, p3]).then(data=>{
  console.log(data)
})

Promise.race

Promise.race 只要有一个 Promise对象 进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

function getCityFromIp(ip) {
  var promise = new Promise(function(resolve, reject){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
    xhr.onload = function(){
      var retJson = JSON.parse(xhr.responseText)  // {"city": "hangzhou","ip": "23.45.12.34"}
      resolve(retJson)
    }
    xhr.onerror = function(){
      reject('获取city失败')
    }
    setTimeout(()=>{
      xhr.send()
    }, Math.random()*1000)

  })
  return promise
}

var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')

//Promise.all, 当所有的 Promise 对象都完成后再执行
Promise.race([p1, p2, p3]).then(data=>{
  console.log(data)
})

参考

Promise 对象
Promise 详解与实现
阮一峰 ES6教程

相关文章

网友评论

    本文标题:ES6 之 Promise

    本文链接:https://www.haomeiwen.com/subject/sjsbwftx.html