Promise异步编程

目录

介绍

概念 

特点:

缺点:

理解

基本流程

 体验使用

抽奖案例

   传统方式

 promise方式:

 读取文本案例

传统模式

promise方式 

 发送ajax请求

传统方式

promise方式

 Promise的属性

promise的状态 

promise 对象的值

Promise的Api

Promise关键点 

Promise如何修改对象的状态


介绍

概念 

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

通俗讲,Promise是一个许诺、承诺,是对未来事情的承诺,承诺不一定能完成,但是无论是否能完成都会有一个结果。

  • Pending 正在做。。。
  • Resolved 完成这个承诺
  • Rejected 这个承诺没有完成,失败了

​ Promise 用来预定一个不一定能完成的任务,要么成功,要么失败

​ 在具体的程序中具体的体现,通常用来封装一个异步任务,提供承诺结果

Promise 是异步编程的一种解决方案,主要用来解决回调地狱的问题,可以有效的减少回调嵌套。真正解决需要配合async/await

特点:

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

​ (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。

缺点:

​ (1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。和一般的对象不一样,无需调用。

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

​ (3)当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

理解

抽象表达:

​    Promise 是一门新的技术(ES6 规范)

​    Promise 是 JS 中进行异步编程的新解决方案 备注:旧方案是单纯使用回调函数

具体表达:

  1. 从语法上来说: Promise 是一个构造函数

  2. 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/ 失败的结果值

promise 的状态

实例对象中的一个属性 『PromiseState』

  • pending 未决定的
  • resolved / fullfilled 成功
  • rejected 失败

promise 的状态改变

    pending 变为 resolved

    pending 变为 rejected

说明: 只有这 2 种, 且一个 promise 对象只能改变一次 无论变为成功还是失败, 都会有一个结果数据 成功的结果数据一般称为 value, 失败的结果数据一般称为 reason

基本流程

Promise异步编程_第1张图片

 体验使用

抽奖案例

     *   点击抽奖按钮  弹出窗口显示是否中奖(30%概率中奖)

     * 若中奖 弹出 恭喜你,中奖了

     * 若未中奖 弹出 未中奖 再接再厉

   传统方式





    
    
    
    Document



    

测试:

Promise异步编程_第2张图片

 promise方式:





    
    
    
    Document



    

测试:

Promise异步编程_第3张图片

 读取文本案例

事先准备一个有内容的txt文本,使用node的fs模块进行读取

传统模式

// 引入 fs模块
const fs = require('fs');

//调用方法读取文件
fs.readFile('./坏蛋.txt', (err, datda) => {
    // 如果失败 抛出错误
    if (err) throw '读取失败'
        // 如果没有出错 输入内容
    console.log(datda.toString())
})

测试:

Promise异步编程_第4张图片

promise方式 

// 引入 fs模块
const fs = require('fs');

//调用方法读取文件
// fs.readFile('./坏蛋.txt', (err, datda) => {
//     // 如果失败 抛出错误
//     if (err) throw '读取失败'
//         // 如果没有出错 输入内容
//     console.log(datda.toString())
// })

//使用promise封装
const p = new Promise((resolve, reject) => {
    fs.readFile('./坏蛋.txt', (err, data) => {
        //判断如果失败
        if (err) reject(err)
            //判断若果成功
        resolve(data)
    })
})

p.then(value => {
    console.log('promise读取')
    console.log(value.toString())
}, reason => {
    console.log('读取失败')
})

测试

Promise异步编程_第5张图片

 再测试读取失败的情况,这里故意把读取文本的位置路径调整为一个错误路径即可

Promise异步编程_第6张图片

再来试试读取多个文本的传统方式

const fs = require('fs')

fs.readFile('./坏蛋.txt', (err, txt1) => {
    fs.readFile('./晴天.txt', (err, txt2) => {
        fs.readFile('./七里香.txt', (err, txt3) => {
            let result = `
            ${txt1}
            ${txt2}
            ${txt3}`
            console.log(result)
        })
    })
})

 测试:

Promise异步编程_第7张图片

promise方式

const fs = require('fs')
console.log('使用promise实现')
const p = new Promise((resolve, reject) => {
    fs.readFile('./晴天.txt', (err, data) => {
        resolve(data)
    })
})

p.then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile('./坏蛋.txt', (err, data) => {
            resolve([value, data])
        })
    })
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile('./七里香.txt', (err, data) => {
            value.push(data)
            resolve(value)
        })
    })
}).then(value => {
    console.log(value.join('\r\n'))
})

测试:

Promise异步编程_第8张图片

 发送ajax请求

准备一个开放接口

教书先生API - 提供免费接口调用平台

在这个平台里随便找一个开放接口进行测试

传统方式





    
    
    
    Promise封装ajax请求方式



    

测试

Promise异步编程_第9张图片

promise方式





    
    
    
    Promise封装ajax请求方式



    

测试

Promise异步编程_第10张图片

 Promise的属性

promise的状态 

实例对象中的一个属性 [PromiseState]

pending 未决定的

resolved/fullfilled 成功

rejected  失败

promise只会有pending->resolved或者pending->rejected这两种情况,

不会从resolved->rejected或者rejected->resolv

promise 对象的值

实例对象中的另一个属性 [PromiseResult]

保存异步任务 [成功/失败]的结果

resolve 成功 

reject 失败

Promise的Api

Promise构造函数: Promise(executor){}

(1) executor 函数: 执行器(resolve,reject)=>{}

(2) resolve 函数: 内部定义成功时调用的函数 value=>{}

(3) reject 函数: 内部定义失败时调用的函数 reason=>{}

说明: executor会在promise内部立即同步调用 异步操作在执行器中执行

Promise.prototype.then(onRsolved,OnRejected)=>{}:

(1) onResolved 函数 成功的回调函数(value)=>{}

(2) onRejected 函数 失败的回调函数 (reason)=>{}

说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调返回一个新的promise对象

Promise.prototype.catch(onRejected)=>{}

onRejected函数 : 失败的回调函数(reason)=>{}

当promise的状态为失败时会执行的回调函数

Promise.resolve(value)=>{}

value成功的数据或promise对象

说明返回一个成功/失败的promise对象





    
    
    
    Document






Promise异步编程_第11张图片

Promise.reject(reason)=>{}

reason:失败的原因

说明:返回一个失败的promise对象

Promise异步编程_第12张图片

 Promise.all(promises)=>{}

promises:promise数组

说明: 返回一个新的promise 只有所有的promise都成功时才会返回一个成功的promise,只要有一个失败的promise就会直接返回一个失败的promise

数组中所有的promise都成功时返回的结果是一个成功的promise,值是所有成功promise的一个集合

Promise异步编程_第13张图片

只要有一个失败的promise的话返回的就是一个失败的promise值是第一个失败的promise的结果

Promise异步编程_第14张图片

 可以看到这里举例p1,p3是成功的promise,p2,p4是失败的promise,但是最后打印的结果却是只有p2失败的信息,所以这里只会返回第一个失败的promise,后面失败的promise没有计入到结果集中

可以理解成一旦失败就中断了,后面的promise自然就不用管成功或失败了

Promise.race 方法:(promises)=>{}

promises:n个promise的数组

说明: 返回一个新的promise,数组中第一个完成的promise的结果状态就是最终的结果状态

就如同数组中的promise在赛跑一样,只获取冠军的状态

Promise异步编程_第15张图片

Promise异步编程_第16张图片

Promise关键点 

Promise如何修改对象的状态

可以通过调用reslove回调将promise状态更改为成功,调用reject和抛出throw将promise状态修改为rejected

Promise异步编程_第17张图片

 Promise异步编程_第18张图片

 Promise异步编程_第19张图片

 能否执行多个回调

 一个promise指定多个成功/失败回调函数时,都会调用吗?

答案:当promise改变为对应的状态时都会调用

多个成功回调

Promise异步编程_第20张图片

 多个失败回调Promise异步编程_第21张图片

注意这里如果 直接使用链式then写多个失败回调时,只会调用第一个失败回调,其他的调用的是成功回调

Promise异步编程_第22张图片

解析:

  1. 创建一个 Promise 对象 p5,状态为 rejected,值为 '失败o(╥﹏╥)o了呀'。
  2. 调用 p5.then(),因为状态为 rejected,所以执行第二个参数函数,输出 '失败o(╥﹏╥)o了呀'。
  3. 调用 p5.then() 的返回值,因为上一个回调函数没有返回值,所以默认返回 undefined,进入下一个 then() 的成功回调,输出 '成功的回调1'。
  4. 调用上一个 then() 的返回值,因为上一个回调函数没有返回值,所以默认返回 undefined,进入下一个 then() 的成功回调,输出 '成功的回调2'。
  5. 最后输出 Promise 对象 p5,状态为 rejected,值为 '失败o(╥﹏╥)o了呀

注意:

当 Promise 连续调用 then() 进行回调时,如果第一个 then() 中状态为 rejected,没有返回值默认返回 undefined,那么会进入下一个 then() 的成功回调而不是失败回调,这是因为 Promise 的状态一旦变为 rejected,就会一直保持 rejected 状态,直到有一个错误处理函数(onRejected)被调用,否则会一直向下传递,直到被捕获或者到达 Promise 链的末尾。

因此,当第一个 then() 的状态为 rejected 时,如果没有提供错误处理函数(onRejected),那么下一个 then() 就会被认为是成功回调,因为它是链中的下一个回调函数。如果想要进入下一个 then() 的失败回调,需要在第一个 then() 中提供错误处理函数(onRejected),或者使用 catch() 方法来捕获错误。

改变状态与指定回调方法的执行顺序问题

 改变promise状态和指定回调函数谁先谁后?

(1) 都有可能 正常情况下是先指定回调再改变状态 但也可以先改状态再指定回调

当改变状态的方法是中是同步任务时,就是先改变状态再去执行then的回调函数

Promise异步编程_第23张图片

但是当改变状态的方法是异步时,就是先执行回调函数再去改变状态

Promise异步编程_第24张图片

 (2) 如何先更改状态再指定回调

   在执行器中直接调用 reslove()/reject()

   延迟更长时间再调用then()

(3) 什么时候才能得到数据

  如果先指定的回调 那么当状态发生改变时 回调函数就会调用 得到数据

  如果先改变的状态 那么当指定回调时 回调函数就会调用 得到数据

then方法返回结果由什么决定

 promise.then() 返回的新promise的结果状态由什么决定

(1) 简单表达: 由then()指定的回调函数执行的结果决定

(2) 详细表达:

    如果抛出异常 新的promise变为rejected reason为抛出的异常

  Promise异步编程_第25张图片

   如果返回的是非promise的任意值 新promise变为resolved,value为返回的值

Promise异步编程_第26张图片

   如果返回的是另一个新的promise 此promise的结果就会成功新的promise结果

Promise异步编程_第27张图片

没有返回值和没有抛出异常的情况

Promise异步编程_第28张图片

result 返回的是一个 Promise 对象,该 Promise 对象是由 p6.then() 返回的。

当调用 p6.then() 时,会返回一个新的 Promise 对象,该对象的状态和值取决于回调函数的执行结果。在这里,回调函数中调用了 console.log() 输出了成功的结果,但是没有返回值,默认返回 undefined,所以该 Promise 对象的状态为 resolved,值为 undefined。

因此,result 返回的是一个状态为 resolved,值为 undefined 的 Promise 对象。

串联多个任务

 (1) promise的then()返回一个新的promise 可以开成then()的链式调用

(2) 通过then的链式调用串联多个同步/异步任务

Promise异步编程_第29张图片

注意这里的第一个then的回调返回结果是一个成功的promise对象,所以第二个then回调第一句打印出的就是第一个then的返回的promise的值,而第二个then没有返回值,所以默认undefined,到了第三个then的回调时没有接收到第二个then的返回值,打印undefined

异常穿透

(1) 当使用promise的then链式调用时 可以在最后指定失败的回调

(2) 前面任何操作出了异常 都会传到最后失败的回调中处理

Promise异步编程_第30张图片

Promise异步编程_第31张图片  

如何中断promise链

(1) 当使用promise的then链式调用时 在中间中断 不再调用后面的回调函数

(2) 解决办法: 在回调函数中返回一个pendding状态的promise对象

Promise异步编程_第32张图片

比如这里的then回调,如果只想执行第一个then回调,后面的两个then回调并不想执行,这里只有一个方法,就是在第一个then回调中返回一个pendding状态的promise 

Promise异步编程_第33张图片

你可能感兴趣的:(前端,es6,异步编程,回调地狱,promise)