ES系列之Promise async 和 await

Promise 对象

概述

promise是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理更强大。

所谓的promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果)。

Promise是一个对象,从它这里可以获取异步操作的信息, Promise提供统一的API,各种异步操作都可以用同样的方法处理。

promise的特点:

  1. 对象的状态不受外界影响,promise对象代表一个异步操作,有三种状态:pending(进行中)Fulfilled(已成功)Rejected(已失败)。只有异步操作的结果才可以决定当前是哪一种操作状态,任何其他操作都无法改变这种状态。

  2. 一旦状态改变就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变只有两种可能:从pending(进行中)变为Fulfilled(已成功)、或者从pending(进行中)变成Rejected(已失败)。状态发生改变就凝固了,不会再变,而是一直保持这个结果,这是就称为Resolved(已定型)。就算改变已经发生,再对Promise对象添加回调函数,也会立即得到这个结果,这个与event完全不同,事件的特点是,如果错过了它,再去监听是得不到结果的。

    有了promise对象,就可以将异步操作以同步操作表示出来,避免了层层嵌套的回调函数。

    Promise的缺点:

    1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。

    2. 如果不设置回调函数,Promise内部抛出错误不会反应到外部。

    3. 当处于Pending状态时,无法得知目前进展到哪一个阶段了。

基本用法

   let promise = new Promise(function(res, rej) {
       // your code...
   })
   
   promise.then(function(value) {
       // res
   }, function(error) {
       // rej
   })

执行顺序

let promise = new Promise(function(resolve, reject) {
  // resolve, reject 才是真正的异步
  console.log('1')
  resolve()
})

promise.then(() => {
  console.log('2')
})

console.log('3')

// 1 3 2

异步加载图片

// 异步加载图片
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    let image = new Image()

    iamge.onload = function () {
      resolve(image)
    }

    image.onerror = function() {
      reject(new Error('不能加载' + url))
    }

    iamge.src = url
  })
}

模拟AJAX

// promise对象实现AJAX
// getJSON 是对XMLHttpReqest对象的封装,用于发出一个JSON数据的HTTP请求
// 并返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数
// 都带有参数

let getJSON = function(url) {
  let promise = new Promise(function(resolve, reject) {
    let client = new XMLHttpRequest()
    client.open('GET', url)
    client.onreadystatechange = handler
    client.responseType = "json"
    client,setRequestHeader('Accept','application/json')
    client.send()

    function handler() {
      if(this.readyState !== 4) {
        return
      }
      if(this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
  })
  return promise
}

getJSON('/posts.json').then(function(json) {
  console.log('Contents:' + json)
},function(error) {
  console.log('error',error)
})

promise中的promise

/**
 * 如果resolve 和 reject 函数都带有参数,那么这些参数会被传递到回调函数。
 * reject函数接收Error对象的实例,表示抛出的错误
 * reslove 函数参数除了正确的值,还有可能是一个Promise对象
 */


let p1 = new Promise(function(resolve, reject) {
  // ...
})

let p2 = new Promise(function(resolve, reject) {
  // ...
  resolve(p1)
})
/**p1 和 p2 都是Promise的实例,但是p2的resolve方法将p1作为参数
 * 即一个异步操作的结果是返回另一个异步操作。
 * p1的状态传递给p2.p1的状态决定了p2的状态
 */
let p1 = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

let p2 = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2.then(result => console.log(result)).catch(error => console.log(error))

/**
 * p1 是 一个Promise,3s之后变成rejected。
 * p2 的状态在1s后改变,resolve方法返回的是p1
 */

Promise.prototype.then()

作用:为了Promise实例添加状态改变时的回调。

promise.then(function(value) {
    // res
}, function(error) {
    // rej
})

// then方法有两个参数,第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。
// then返回的是一个新的Promise实例

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误的回调函数。

另外then方法指定的回调函数如果在运行中抛出错误,也会被catch捕获。

一般来说,不要在then方法中定义Rejected状态的回调(即then的第二个参数),而应该总是使用catch方法。

p2.then(result => console.log(result))
.catch(error => console.log(error))
// .catch 处理 p2 和前一个回调函数运行时发生的错误

// ====> 等同于
p2.then(result => console.log(result))
.then(null, err => console.log(err))

实例

 var pro = new Promise(function(){
      var a = 1;
})
console.log(pro); 

读文件的方法

const fs = require('fs')

// 总结:只要 new 了一个具体的异步操作,这个异步操作被创建的一瞬间,就会立即执行;
function readFileByPath(fpath) {
  const p = new Promise(function() {
    fs.readFile(fpath, 'utf-8', (err, result) => {
      if (err) return console.log('读文件失败:' + err.message)
      console.log(result)
    })
  })
}

readFileByPath('./files/3.txt') // 文件必须存在

使用.then()进一步封装读文件的操作

封装原则:不要在方法内部显示结果,要把结果返回给调用者,不要提调用者做决定!!

 使用.catch()进一步封装读文件的操作

const fs = require('fs')

function readFileByPath(fPath) {
  return new Promise(function(resolve,reject){
    fs.readFile(fPath, 'utf-8', (err, result) => {
      if (err) return reject(arr)
      resolve(result)
    })
  })
}

// 一般.then()方法中,失败的回调可以省略,但是省略以后读取文件失败时,无法接收结果
// 这是,我们可以使用.catch() 来指定失败的回调
/* --------------读一个-------------------- */
readFileByPath('./files/1.txt')
  .then(function(result) {
    console.log(result)
  })
  .catch(err => console.log(err.message))

/* ---------------读多个----------------------- */
readFileByPath('./files/1.txt')
  .then(function(result){
    console.log(result)
    return readFileByPath('./files/2.txt')
  })
  .then(function(result){
    console.log(result)
    return readFileByPath('./files/3.txt')
  })
  .catch(err => console.log(err.message))

 async和await

const fs = require('fs')

function readFileByPath(fPath) {
  return new Promise(function(resolve,reject){
    fs.readFile(fPath, 'utf-8', (err, result) => {
      if (err) return reject(arr)
      resolve(result)
    })
  })
}

// async 用来修饰异步方法
// await 只能用在被 async 修饰的方法中
// 同时,await 是用来修饰 Promise 实例对象的;简化promise对象

console.log("开始");
async function readAll () {
  console.log("方法头部");
  const result1 = await readFileByPath('./files/1.txt')
  console.log(result1)
  const result2 = await readFileByPath('./files/2.txt')
  console.log(result2)
  const result3 = await readFileByPath('./files/3.txt')
  console.log(result3)
  console.log("方法尾部");
}
console.log("结束");

readAll()

运行顺序:

原因:

/* async修饰的 readAll()方法异步方法,在调用这个readAll()方法的时候,js主线程进入这个方法
这时,还是主线程在执行,输出“方法头部”,然后在 await是异步操作,这是主线程就退出这个方法,
执行“结束”,然后就是异步的方法顺序执行 */

猜你喜欢

转载自www.cnblogs.com/houfee/p/9945240.html