promise异步编程的解决方案,async/await实现原理

promise的用法

异步编程的解决方案

首先我们知道,由于 JavaScript 是单线程执行模型,因此必须支持异步编程才能提高运行效率。异步编程的语法目标是让异步过程写起来像同步过程。

举个栗子,有三个文件1.txt 2.txt 3.txt 分别是111 222 333,按顺序读取出数据 111=>222=>333,由于readFile读取文件是异步的,所以按照这个方式会导致顺序不一致

// 读取文件 1.txt
fs.readFile('./files/1.txt','utf-8',(err,dataStr)=>{
     if(err) return console.log(err.message) //读取文件 1 失败
     console.log(dataStr)  //读取文件 1 成功
})
//  读取文件 2.txt
fs.readFile('./files/2.txt','utf-8',(err,dataStr)=>{
    if(err) return console.log(err.message)  // 读取文件 2 失败
    console.log(dataStr) //读取文件 2 成功
})
//  读取文件 3.txt
fs.readFile('./files/3.txt','utf-8',(err,dataStr)=>{
    if(err) return console.log(err.message) // 读取文件 3 失败
    console.log(dataStr) // 读取文件 3 成功
})
// ---------------------------------------------------------------
// 打印结果为:111 333 222

回调函数

这个时候我们可以用回调函数解决

fs.readFile('./files/1.txt','utf-8',(err,dataStr)=>{
    if(err) return console.log(err.message)
    console.log(dataStr)
    fs.readFile('./files/2.txt','utf-8',(err,dataStr)=>{
        if(err) return console.log(err.message)
        console.log(dataStr)
        fs.readFile('./files/3.txt','utf-8',(err,dataStr)=>{
            if(err) return console.log(err.message)
            console.log(dataStr)
         })
     })
})
// 打印结果为:111 222 333

用这种方法确实是可以实现异步编程,但是回调函数最大的问题是容易形成回调地狱,即多个回调函数嵌套,降低代码可读性,增加逻辑的复杂性,容易出错。

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

回调地狱

说promise之前,我们先来说一下到底什么是回调地狱,官方语言是 多层回调函数的相互嵌套,就形成了回调地狱。

那么我们用大白话来说 回调地狱就是因为多个串连的异步操作导致的,串连就是第二个是在第一个成功之后,第三个是在第二个成功之后,最终形成回调函数的嵌套。

举栗说明

比如说现在要发三个异步请求,但是我的条件是第二个请求需要第一个请求成功的一部分数据,第三个请求需要第二个请求成功的一部分数据才能发,我想得到的是最后第三个请求返回出来成功的结果,那这个时候我们如果用回调函数的方式去写,那我的第二个请求的发送是在第一个请求的回调函数里面,那我发第二个请求的又指定了一个回调函数,那就形成了回调函数的嵌套。这就是回调地狱

示例代码如下:

// -----------异步嵌套--------------------------------------------
fs.readFile('./files/1.txt','utf-8',(err,dataStr)=>{
    if(err) return console.log(err.message)
    console.log(dataStr)
    fs.readFile('./files/2.txt','utf-8',(err,dataStr)=>{
        if(err) return console.log(err.message)
        console.log(dataStr)
        fs.readFile('./files/3.txt','utf-8',(err,dataStr)=>{
            if(err) return console.log(err.message)
            console.log(dataStr)
         })
     })
})

promise

为解决回调函数的不足,JS中进行异步编程的新解决方案 promise

首先来了解一下什么是 promise

promise的概念

1.Promise 是一个构造函数

我们可以创建 Promise 的实例 const p = new Promise()

每一个new 出来的 Promise 实例对象,都代表一个异步操作

2.Promise 是一个构造函数的实参也是一个函数

当new Promise时,这个实参函数会立即执行

3.Promise.prototype 上包含一个 .then() 方法

每一次 new Promise() 构造函数得到的实例对象,

都可以通过原型链的方式访问到 .then() 方法,例如 p.then()

4.then() 方法用来预先指定成功和失败的回调函数

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

p.then(result => { }, error => { })

调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

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

成功的回调函数会赋值 给 构造函数实参函数的 resolve

失败的回调函数会赋值 给 构造函数实参函数的 reject

图例说明

首先,创建一个Promise实例对象,通过new Promise实现,new Promise时,会返回Promise实例p。在new的时候指定了一个执行器函数,异步操作封装在执行器函数里面,当前是pending状态,promise 有三种状态:Pending(初始状态)、Resolved(成功,又称 Fulfilled)、Rejected(失败)。

第二步执行异步操作,成功了执行resolve,此时promise状态为resolved状态,失败了执行rejecte,此时promise状态为Rejected状态,状态只能是三种中的一种,不能同时有两种状态。

第三步实例p身上有一个方法then,then这个方法在原型上,then方法中有两个回调函数: 第一个回调函数代表成功,第二个回调函数代表失败 成功了会调用成功的回调函数,这个成功的函数会赋值给resolve。失败了会调用失败的回调函数,这个成功的函数会赋值给resolve 。

有时then()失败的回调函数可以不写,可以用.catch用来捕获错误

最后会返回一个新的promise对象。

如何声明一个Promise

new Promise(function(resolve, reject){ })

如果想让Promise成功执行下去,需要执行resolve,如果让它失败执行下去,需要执行reject

new Promise(function(resolve, reject) { 
    resolve('success')  // 成功执行
}).then(result => {
    alert(result)
})
​
new Promise(function(resolve, reject) { 
    reject('fail')  // 成功执行
}).then(result => {
    alert(result)
}).catch(error => {
     alert(error)
})

如果想终止在某个执行链的位置,可以用Promise.reject(new Error())

new Promise(function(resolve, reject) {
    resolve(1)
}).then(result => {
    return result + 1
}).then(result => {
    return result + 1
}).then(result => {
  return  Promise.reject(new Error(result + '失败'))
   // return result + 1
}).then(result => {
    return result + 1
}).catch(error => { 
    alert(error)
})

用promise创建具体的异步操作

基于 Promise 按顺序读取文件的内容

//具体实现
import fs from 'fs'
​
const p = new Promise(function(resolve,reject){
​
    fs.readFile("./files/11.txt",'utf-8',(err,dataStr)=>{
        if(err) return reject(err);
        resolve(dataStr)
    })
})
​
p.then(function(data){
    console.log(data);
},function(err){
    console.log(err.message);
})

获取 .then 的两个实参

通过 .then() 指定的成功和失败的回调函数,可以在 function 的形参中进行接收,示例代码如下:

调用 resolve 和 reject 回调函数

Promise 异步操作的结果,可以调用 resolve 或 reject 回调函数进行处理。示例代码如下:

执行顺序示意图

通过 .catch 捕获错误

在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:

import fs from 'fs'
const p = new Promise(function(resolve,reject){
    fs.readFile("./files/11.txt",'utf-8',(err,dataStr)=>{
        if(err) return reject(err);
        resolve(dataStr)
    })
})
​
// catch捕获错误
p.then(function(data){
    console.log(data);
}).catch(function(err){
    console.log(err.message);  //ENOENT: no such file or directory
})
  
​

解决异步操作的完整过程

import fs from 'fs' 
​
function read(fatch){
   let p = new Promise(function(resolve,reject){
        fs.readFile(fatch,'utf-8',(err,dataStr)=>{
            if(err) return reject(err)
            resolve(dataStr)
         })
​
    })
    return p
​
}
​
read('./files/1.txt')
.then(function(res1){
    console.log(res1)
     return read('./files/2.txt')
})
.then(function(res2){
    console.log(res2)
    return read('./files/3.txt')
})
.then(function(res3){
    console.log(res3)
})
.catch(function(err){
    console.log(err.message)
})

总结:promise通过.then方法的链式调用,来避免回调地狱,但是最大问题是代码冗余,语义不清晰。

2.async、await

为了解决 Promise 的问题,async、await 在 ES7 中被提了出来,async、await是基于promise进一步封装的,是异步操作的终极解决方案

基本使用

//具体实现
async 函数名() {
    const res1 = await promise实例1
    const res2 = await promise实例2
    
    console.log(res1,res2)  //按照顺序打印出结果
}

async/await的返回值问题

async声明的函数返回值 永远是promise实例

await后面跟promise实例

//具体实现
// 1.异步函数返回值问题: 永远返回的是一个promise实例
// 1.1 没有return         空promise 没有太多实质意义
 async function show(){
     
 }
 console.log(show())  
​
// 1.2 return 非promise     将数据包装成了promise
 async function show(){
     return 3
 }
 console.log(show())  
​
//   1.3 return promise       返回的就是该promise
 async function show(){
     return promise实例
 }
 console.log(show())  

使用 async/await 简化 Promise 异步操作的示例代码如下

​
//  async/await  解决按需读取文件
​
async function getFile(){
    const p1 = await read('./files/1.txt')
    const p2 = await read('./files/2.txt')
    const p3 = await read('./files/3.txt')
    console.log(p1,p2,p3);
}
getFile()  //111  222  333

async/await 的使用注意事项

① 如果在 function 中使用了 await,则 function 必须被 async 修饰

② 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行

console.log('a');
async function getFile(){
   console.log("b");
   const p1 = await read('./files/1.txt')
   const p2 = await read('./files/2.txt')
   const p3 = await read('./files/3.txt')
   console.log("c");
   console.log(p1,p2,p3);
   console.log("d");
}
console.log("e");
getFile()
console.log("f");
//结果: a e b f c 111 222 333 d

async 和 await实际上就是让我们像写同步代码那样去完成异步操作

await 表示强制等待的意思,await关键字的后面要跟一个promise对象,它总是等到该promise对象resolve成功之后执行,并且会返回resolve的结果

 async test () {
      // await总是会等到 后面的promise执行完resolve
      // async /await就是让我们 用同步的方法去写异步
      const result = await new Promise(function (resolve, reject) {
        setTimeout(function () {
          resolve(5)
        }, 5000)
      })
      alert(result)
    }

上面代码会等待5秒之后,弹出5

async 和 await必须成对出现

由于await的强制等待,所以必须要求使用await的函数必须使用async标记, async表示该函数就是一个异步函数,不会阻塞其他执行逻辑的执行

async test () {
  const result = await new Promise(function(resolve){  
    setTimeout(function(){
      resolve(5)
    },5000)
  })
  alert(result)
    }
test1(){
  this.test()  // 异步函数
  alert(1)
}

通过上面的代码我们会发现,异步代码总是最后执行,标记了async的函数并不会阻塞整个的执行往下走

如果你想让1在5弹出之后再弹出,我们可以这样改造

   async test1(){
     await this.test()
      alert(1)
   }
// 这充分说明 被async标记的函数返回的实际上也是promise对象

如果promise异常了怎么处理?

promise可以通过catch捕获,async/ await捕获异常要通过 try/catch

   async  getCatch () {
      try {
        await new Promise(function (resolve, reject) {
          reject(new Error('fail'))
        })
        alert(123)
      } catch (error) {
        alert(error)
      }
   }

async / await 用同步的方式 去写异步

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值