Promise异步操作

1.回调地狱

​ 多层回调函数的相互嵌套,就形成了回调地狱,如:

setTimeout(() => {     // 第一层回调函数

    console.log('延迟1秒后输出')
    setTimeout(() => {      // 第二层回调函数

        console.log('延迟2秒后输出')
        setTimeout(() => {      // 第三层回调函数

            console.log('延迟3秒后输出')
        }, 3000)
    }, 2000)
}, 1000)

回调地狱的缺点:

  • 代码耦合性太强,牵一发而动全身,难以维护
  • 大量的代码冗余相互嵌套,代码的可读性差

为了解决回调地狱的问题,ES6中新增了Promise概念。

2.Promise

Promise是一个构造函数

const p = new Promise()
  • new 出来的Promise实例对象,代表一个异步操作

查看Promise实例

console.dir(promise)
  • 可以看到prototype上有then()方法

Promise的.then()方法

Promise.prototype上挂载了.then()方法,每一次new Promise()得到的实例对象,都可以通过原型链的方式访问到.then()方法 如 p.then()。

.then(成功的回调函数,失败的回调函数)

p.then(result=>{},err=>{})
  • .then()方法用来预先指定成功失败的回调函数
  • 调用.then()方法时,成功的回调函数是必选的,失败的回调函数是可选的

(1)基于回调函数读取文件

import fs from 'fs'

fs.readFile('./file/01.txt', 'utf8', (err1, res1) => {

    if (err1) return console.log(res1.message)
    console.log(res1)     // 123    读取文件1成功
    fs.readFile('./file/02.txt', 'utf8', (err2, res2) => {

        if (err2) return console.log(err2.message)
        console.log(res2)  // 456           读取文件2成功
        fs.readFile('./file/03.txt', 'utf8', (err3, res3) => {

            if (err3) return console.log(err3.message)
            console.log(res3)   // 789              读取文件3成功
        })
    })
})
  • 可以看到出现了回调地狱的问题

(2)基于Promise异步读取文件

​ 由于node.js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持pormise调用方式。因此,

需要先安装then-fs第三方包,从而支持我们基于Promise方式读取文件内容

import thenFs from 'then-fs'

thenFs.readFile('./file/01.txt', 'utf8').then(res1 => { console.log(res1) })
thenFs.readFile('./file/02.txt', 'utf8').then(res2 => { console.log(res2) })
thenFs.readFile('./file/03.txt', 'utf8').then(res3 => { console.log(res3) })
  • 调用then-fs提供的readFile()方法,可以异步读取文件内容,thenFs.readFile()的返回值是Promise实例对象
  • 因此可以调用.then()为每个Promise异步操作指定成功和失败的回调函数
  • 由于是异步操作,得到的顺序不一

(3)基于Promise的链式调用

import thenFs from "then-fs"

thenFs.readFile('./file/10.txt', 'utf8').catch(err => {
    console.log(err.message)    
}).then(r1 => {
    console.log(r1)            
    return thenFs.readFile('./file/02.txt', 'utf8')     
}).then(r2 => {                                         

    console.log(r2)             
    return thenFs.readFile('./file/03.txt', 'utf8')     
}).then(r3 => {                                         
    console.log(r3)             
})
  • 在第一个.then()中返回了一个新的Promise实例对象,继续调用.then(),为上一个.then的返回值(新的Promise实例),指定成功之后的回调函数
  • Promise的链式操作中如果发生了错误,可以使用Promise.prototype.catch方法进行捕获处理
  • 如果不希望前面的错误导致后续的.then无法正常执行,则可以将.catch的调用提前(.catch放在最后的话前面产生的错误会导致后面.then无法被执行)

.then()方法的特性:

  • 如果上一个.then()方法中返回了一个新的Promise实例对象,则可以通过下一个.then()继续进行处理。

  • 通过.then()方法的链式调用,解决回调地狱问题

(4)Promise.all()

Promise.all()方法会发起并行的Promise异步操作,等所有的异步操作全部结束后,才会执行下一步的.then操作 (等待机制)。

import thenFs from "then-fs"

const promiseArr = [

    // 三个异步读文件的promise实例
    thenFs.readFile('./file/01.txt', 'utf8'),
    thenFs.readFile('./file/02.txt', 'utf8'),
    thenFs.readFile('./file/03.txt', 'utf8')
]

Promise.all(promiseArr).then(res => {    // promise实例的顺序就是最终拿到的结果的顺序
    console.log(res)    // ['123','456','789']
}).catch(err => {
    console.log(err.message)
})
  • Promise.all() 等待所有的异步操作完成,拿到所有操作的结果

(5)Promise.race()

Promise.race()方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就会立即执行下一步的.then操作 (赛跑机制)。

import thenFs from "then-fs"

const promiseArr = [

    // 三个异步读文件的promise实例
    thenFs.readFile('./file/01.txt', 'utf8'),
    thenFs.readFile('./file/02.txt', 'utf8'),
    thenFs.readFile('./file/03.txt', 'utf8')
]

Promise.race(promiseArr).then(res => {  // 只要任何一个异步操作完成,就立即执行成功的回调函数
    console.log(res)
}).catch(err => {
    console.log(err.message)
})
  • Promise.race() 哪个异步操作执行得快,就拿它的结果(得到执行最快的异步操作)

(6)基于Promise封装异步读文件的方法

import fs from 'fs'

function getFile(fpath) {
    // resolve是成功的回调函数,reject是失败的回调函数
    return new Promise(function (resolve, reject) {    // 形式上的异步操作

        fs.readFile(fpath, 'utf8', (err, dataStr) => {      // 具体的异步操作

            if (err) return reject(err)     // 如果读取失败,则调用失败的回调函数
            return resolve(dataStr)         // 如果读取成功,则调用成功的回调函数
        })
    })
}

getFile('./file/01.txt').then((res1) => { console.log(res1) }).catch(err => { console.log(err.message) })
  • 方法接收一个形参 fpath,代表要读取文件的路径
  • 方法的返回值为Promise实例对象
  • new Promise() 只是创建了一个形式上的异步操作
  • 如果想要创建具体的异步操作,则需要在new Promise()构造函数期间,传递一个function()函数, 将具体的异步操作定义到function函数内部

3.async/await

async/await是ES8引入的新语法,用来简化Promise异步操作。在async/await出现之前,我们只能通过链式.then()方式处理promise异步操作。

链式.then()调用的优点:

  • 解决了回调地狱的问题

链式.then()调用的缺点:

  • 代码冗余,阅读性差,不易理解

(1)使用async/await简化Promise异步操作

import thenFs from "then-fs"

async function getFile() {         
    
    const r1 = await thenFs.readFile('./file/01.txt', 'utf8')        
    console.log(r1)

    const r2 = await thenFs.readFile('./file/02.txt', 'utf8')
    console.log(r2)

    const r3 = await thenFs.readFile('./file/03.txt', 'utf8')
    console.log(r3)

}

getFile()
  • 如果方法内部用到了await,这个方法就必须被async修饰
  • 加了await后,thenFs.readFile()返回的就不再是promise实例,而是得到了异步操作后具体的内容
  • 有了async和await就不需要.then()来获得最后的结果了

(2)async和await的注意事项

import thenFs from "then-fs"

console.log('A')
async function getFile() {

    console.log('B')
    const r1 = await thenFs.readFile('./file/01.txt', 'utf8')
    const r2 = await thenFs.readFile('./file/02.txt', 'utf8')
    const r3 = await thenFs.readFile('./file/03.txt', 'utf8')
    console.log(r1)
    console.log(r2)
    console.log(r3)
    console.log('D')

}

getFile()		// A B C 123 456 789 D
console.log('C')	
  • 在async方法中,第一个await之前执行的代码会同步执行,await之后执行的代码会异步执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值