JS基础(一)-畅游promise世界

本文主要介绍了我的对promise的一些粗浅理解,若有不正确的地方,还请指正。css

一. Promise是什么

从JS语法层面上讲,Promise是一个函数,能够经过new关键字进行调用而后生成一个对象,这个对象被成为promise对象。promise对象有三种状态,为pedding、resolved、rejected,这是promise的重要机制。 从功能的角度来说,Promise是一个容器,它里面包含着一个将来才会获得的(一般是异步)结果。 Promise帮助调用者拿回了调用回调函数的主动权,而不是把主动权交给第三方函数。前端

二. promise解决了什么问题

2.1 回调的涉及的信任问题

1. 使用回调风格的异步处理

如下关于callback函数的执行有几种信任问题。vue

function getData(url, callback) {
  let req = new XMLHttpRequest()
  req.open('GET', url, true)
  req.onload = function () {
    if (req.status === 200) {
      // 使用回调处理会存在几种潜在的问题
      // 1. 过早或者过晚调用
      // 2. 干脆不调用
      // 3. 调用了不少次
      // 4. 没有给回调函数传入指定的参数
      callback(req.responseText)
    } else {
      console.log(new Error(req.statusText))
    }
  }
  req.onerror = function () {
    console.log(new Error(req.statusText))
  }
  req.send()
}

function callback(text) {
  console.log(text)
}

let url = 'http://api.myjson.com/bins/16hvos'
getData(url, callback)
复制代码

2. 使用promise的异步处理

function getData(url) {
  return new Promise(function (resolve, reject) {
    let req = new XMLHttpRequest()
    req.open('GET', url, true)
    req.onload = function () {
      if (req.status === 200) {
        resolve(req.responseText)
      } else {
        reject(new Error(req.statusText))
      }
    }
    req.onerror = function () {
      reject(new Error(req.statusText))
    }
    req.send()
  })
}

let url = 'http://api.myjson.com/bins/16hvos'
getData(url).then(function onFullfilled(value) {
  console.log(value)
}).catch(function onRejected(value) {
  console.log(value)
})
复制代码

2.2 回调地狱

回调地狱会致使代码晦涩难懂,不易维护,错误处理复杂。ios

1. 回调地狱(callback hell)示例代码

// 先执行taskA 获得符合预期结果; 执行taskB, 获得预期结果,执行taskC, 获得预期结果,接着处理。。。
function taskA(callback) {
  let result = 1
  setTimeout(() => {
    callback(result)
  }, 2000)
}

function taskB(callback) {
  let result = 2
  setTimeout(() => {
    callback(result)
  }, 2000)
}

function taskC(callback) {
  let result = 3
  setTimeout(() => {
    callback(result)
  }, 2000)
}

taskA(function (res) {
  if (res === 1) {
    taskB(function (res) {
      if (res === 2) {
        taskC(function (res) {
           if (res === 3) {
             console.log(res)
           }
        })
      }
    })
  }
})
复制代码

能够看出每执行一个函数都须要给其一个回调函数,并且代码末尾有不少括号的嵌套,虽然可使用必定方法能够改善这种嵌套的方法,可是依然代码依然不够易读。好比采用以下方法抽离一部分代码git

function a(res) {
  if (res === 1) {
    taskB(b)
  }
}

function b (res) {
  if (res === 2) {
    taskC(c)
  }
}

function c(res) {
  if (res === 3) {
    console.log(res)
  }
}

taskA(a) // 3
复制代码

即便采用以上方式处理,阅读此代码仍然让人头大。es6

2. 使用promise处理回调地狱

// taskA执行,拿到预期结果,返回一个resolved的promise对象
function taskA() {
  let result = 1
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      if (result === 1) {
        resolve(result)
      } else {
        reject(new Error('fail'))
      }
    }, 2000)
  })
}

// 一个resolved状态的promise对象能够在其then方法中拿到异步处理的结果
// 在then方法中的回调函数中使用return会新生成一个resolved状态的promise对象
taskA().then(function (result) {
  return result
}).then(function taskB() {
  let result = 2
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(result)
    }, 2000)
  })
}).then(function taskC() {
  let result = 3
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(result)
    }, 2000)
  })
}).then(function (result) {
  console.log(result)
})
复制代码

三. promise的使用

3.1 建立一个promise对象

1. 构造函数形式

Promise函数接受一个函数做为参数,这个函数有两个参数,resolve和reject, 用来改变所生成的promise对象的状态。这个函数参数会当即执行。github

let promise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    resolve(42)
  }, 2000)
})
promise.then(function (value) {
  console.log(value) // 42
})

Object.getPrototypeOf(promise) === Promise.prototype // true
复制代码

2. Promise.resolve

Promise.resolve() 直接返回一个已解析状态的promise对象。vuex

var promise = Promise.resolve(42)
// 至关于
var promise = new Promise(function (resolve, reject) {
  resolve(42)
})
复制代码

3. Promise.reject

Promise.reject 直接返回一个已拒绝状态的promise对象。json

var promise = Promise.reject(new Error('ok'))
// 至关于
var promise = new Promise(function (resolve, reject) {
  reject(new Error('fail'))
})
复制代码

3.2 promise#then 和 promise#catch

1. promise#then

(1) 每个promise对象都有then方法,当这个promise对象的状态变为resolved时,会执行then中注册的回调函数。axios

let promise = Promise.resolve(42)
promise.then(function (value) {
  console.log(value) //42
})
复制代码

(2) promise对象的then方法会返回一个新的promise对象,所以咱们能够在新生成的promise对象中继续使用then方法。

let promise = Promise.resolve(42)
promise.then(function (value) {
  console.log(value)
}).then(function (value) {
  console.log('ok')
})
复制代码

根据then方法的特性能够实现promise对象的链式调用。

let promise = Promise.resolve()

function taskA() {
  console.log('taskA')
}

function taskB() {
  console.log('taskB')
}

function onRejected(error) {
  console.log(error)
}

promise
  .then(taskA)
  .then(taskB)
  .catch(onRejected)
// taskA taskB 
复制代码

以上例子中,实现了promise的链式调用。当taskA和taskB中没有抛出错误时,程序会一直执行下去,其中catch是为了捕获taskA或taskB出现的异常。若是taskA或者taskB中出现了错误,那么catch中的回调函数就会执行。以下:

let promise = Promise.resolve()

function taskA() {
  throw new Error('fail')
}

function taskB() {
  console.log('taskB')
}

function onRejected(error) {
  console.log(error)
}

promise
  .then(taskA)
  .then(taskB)
  .catch(onRejected)
// Error fail
复制代码

若是taskA出现了一个错误,那么taskB将不会执行,会执行调用catch中的回调函数。 3). 链式调用传值 想要实现链式调用的传值,只须要在then中的回调函数给出返回值,Promise就会自动将这个返回值解析为一个promise对象。

let promise = Promise.resolve(10)

function taskA(value) {
  return value + 10
}

function taskB(value) {
  return value + 10 
}

function taskC(value) {
  console.log(value)
}

promise
  .then(taskA)
  .then(taskB)
  .then(taskC) // 30
复制代码

2. promise#catch

当一个promise的状态为rejected时,那么它的catch方法中的回调函数将会被执行。catch方法其实就是then方法中第二个回调函数。

let promise = Promise.reject('fail')
promise.catch(function (error) {
  console.log(error) // fail
})

// 至关于
promise.then(undefined, function (error) {
  console.log(error) // fail
})
复制代码

catch也会返回一个promise对象。

3.3 Promise.all 和 Promise.race

1. Promise.all

Promise.all接受一个由promise对象组成的数组参数,当每一个promise的状态都变成resoved或rejected时,Promise.all返回的promise对象的状态才会改变(resolved或者rejected)。

// getData函数返回一个promise, 而且函数体内的Promise函数会当即执行,即当即执行函数体内的异步任务
function getData(url) {
  return new Promise(function (resolve, reject) {
    var req = new XMLHttpRequest()
    req.open('GET', url, true)
    req.onload = function () {
      if (req.status === 200) {
        return req.responseText
      } else {
        return new Error(req.statusText)
      }
    }
    req.onerror = function () {
      reject(new Error(req.statusText))
    }
    req.send()
  })
}

// 错误写法
// getData函数前加了async关键字,所以它也返回一个promise对象,可是函数体内的请求时异步的,
// getData会当即执行,而后再去执行异步任务,所以getData返回的promise永远是resolved undefined
async function getData(url) {
  var req = new XMLHttpRequest()
  req.send()
  req.open('GET', url, true)
  req.onload = function () {
    if (req.status === 200) {
      console.log('ok')
      return req.responseText
    } else {
      return new Error(req.statusText)
    }
  }
  req.onerror = function () {
    reject(new Error(req.statusText))
  }
}

Promise.all([
  getData('http://azu.github.io/promises-book/json/comment.json'),
  getData('http://azu.github.io/promises-book/json/people.json')
]).then(res => {
  console.log(res)
})
复制代码

2. Promise.race

Promise.race也接受一个由promise对象组成的数组参数,当其中的一个promise对象的状态变为resolved或是rejected时,Promise.race返回的promise对象才会改变(resolved或者rejected)。

3.4 只执行异步操做的promise 和 async/await

1. 一个当即变为resolved状态的promise对象,promise.then中注册的回调函数仍是会被以异步方式调用。
function asyncPromise () {
  return new Promise(resolve => {
    resolve(3)
  })
}

function test() {
  asyncPromise().then(function (value) {
    console.log(value)
  })
  console.log(1)
}

test()
console.log(2)
// 输出顺序为 1 2 3
复制代码

2. 一个function前加了async关键字,那么这个函数会返回一个promise对象

能够把async当作是一个返回值为promise对象的函数的语法糖。

async function test() {
  return 1
}
test() // Promise {<resolved>: 1} google浏览器的打印结果

// 至关于
function test() {
  return Promise.resolve(1)
}
test() // Promise {<resolved>: 1} google浏览器的打印结果
复制代码

3. await 关键字后面若是是一个promise对象,那么它会把这个promise对象解析后的结果取出来,而且其后面的不论是同步代码仍是异步代码都会等到这个promise对象解析出来后(由pedding变为resolved或rejected),才会执行后面的代码。

await 让异步代码变为了同步(同步执行)。

function asyncFunction () {
  return new Promise(resolve => {
    resolve(2)
  })
}

async function test() {
  let value = await asyncFunction()
  console.log(value)
  console.log(3)
}

test()
console.log(1)
// 打印结果为 1 2 3 
复制代码

4. 项目实践

采用两种方式模拟一个通过vuex获取数据的过程。 (1) 直接返回promise对象的方式

// src/People.vue
created () {
  this.$store.dispatch('GetPeople', { page: 1, size: 20 }).then(response => {
    console.log(response) // [{ id: 1, name: 'Joe'}]
  })
}

// src/store/people/actions.js
GetPeople({ commit }, payload) {
  return new Promise(function (resolve, reject) {
    axios.get('http://www.example.com/people', { payload })
      .then(function (response) {
         resolve(response) // response = [{ id: 1, name: 'Joe'}]
      }).catch(function (error) {
         reject(error)
      })
  })
}
复制代码

(2) async / await

// src/People.vue
async created() {
  await this.$store.dispatch('GetPeople', { page: 1, size: 20 })
  // [{ id: 1, name: 'Joe'}]
}

// src/store/people/actions.js
async GetPeople({ commit }, payload) {
  const response = await axios.get('http://www.example.com/people', { payload })
  return response
}
复制代码

参考:

you don't know js

promise 迷你书

<关于咱们>

咱们是来自帝都的一枚前端程序猿 + 一枚前端程序媛。

这里发布的文章是咱们对学习内容的总结,预计会每周至少会更新一篇。

目前咱们学习计划是: 小程序实战 => vue 进阶用法 => vue 原理 => css 基础 => es6 => js 深刻

另外,工做中用到的一些技术和完成的功能,咱们也会及时总结更新在这里

如文章有错误或表述不清晰,欢迎各位留言反馈~~

相关文章
相关标签/搜索