亲宝软件园·资讯

展开

JavaScript Promise回调地狱

一个水瓶座程序猿. 人气:0

Promise

Promise能够处理异步程序。

回调地狱

JS中或node中,都大量的使用了回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来,嵌套的层数特别多,就会形成回调地狱 或者叫做 横向金字塔。

案例:有a.txt、b.txt、c.txt三个文件,使用fs模板按照顺序来读取里面的内容,代码:

// 将读取的a、b、c里面的内容,按照顺序输出
const fs = require('fs');
// 读取a文件
fs.readFile('./a.txt','utf-8',(err,data)=>{
    if(err) throw err;
    console.log(data);
    // 读取b文件
    fs.readFile('./b.txt','utf-8',(err,data)=>{
       	if(err) throw err;
        console.log(data);
        // 读取c文件
        fs.readFile('./c.txt','utf-8',(err,data)=>{
            if(err) throw err;
            console.log(data)
        })
    })
})

案例中,循环嵌套的代码越来越多,这就是 地狱回调

Promise简介

Promise简单使用

Promise是“承诺”的意思,实例中,它里面的异步操作就相当于一个承诺,而承诺就会有两种结果,要么完成了承诺的内容,要么失败。

所以,使用Promise,分为两大部分,首先是有一个承诺(异步操作),然后再兑现结果。

第一部分:定义"承诺"

// 实例化一个Promise,表示定义一个容器,需要给它传递一个函数作为参数,而该函数又有两个形参,通常用resolve和reject表示。该函数里面可以写异步请求的代码
// 换个角度,也可以理解为定下了一个承诺诺
let p = new Promise((resolve,reject)=>{
    // 形参resolve,单词意思是 完成
    // 实参reject,单词意思是 失败
    fs.readFile('./a.txt','utf-8',(err,data)=>{
        if(err){
            // 失败,就告诉别人,承诺失败了
            reject(err);
        }else{
            // 成功,就告诉别人,承诺实现了
            resolve(data);
        }
    }
})

第二部分:获取"承诺"的结果

// 通过调用 p 的then方法,可以获取到上述 "承诺" 的结果
// then方法有两个函数类型的参数,参数1表示承诺成功时调用的函数,参数2可选,表示承诺失败时调用的函数
// p.then(
//	(data)=>{},  // 函数类型的参数,用于获取承诺成功后的数据
//   (err)=>{}   // 函数类型的参数,用于承诺失败后的错误信息
//)
p.then(
	(data)=>{
        console.log(data);
    },
    (err)=>{
        console.log(err);
    }
)

三种状态

当达到最终的fulfilled 或 rejected 时,promise的状态就不会再改变了。

特点

当调用 resolve的时候,Promise 将到达最终的状态。 达到最终状态之后,Promise的状态就不会再改变了。

多次调用resolve函数,只有第一次有效,其他的调用都无效。

const fs = require('fs');
// 1.创建Promise对象:(承诺)
let p = new Promise((resolve,reject)=>{
    resolve(123);
    resolve(456);  // 这次的调用无效
});
// 2.获取异步任务的结果
// p.then(函数1,[函数2]);
p.then(res =>{
    console.log(res);  // 123
},err =>{
    console.log(err);
})

then方法的链式调用

const fs = require('fs');
// promise 承诺
let p1 = new Promise((resolve, reject) => {
  fs.readFile('./a.txt', 'utf-8', (err, data) => {
    err ? reject(err) : resolve(data.length);
  });
});
let p2 = new Promise((resolve, reject) => {
  fs.readFile('./b.txt', 'utf-8', (err, data) => {
    err ? reject(err) : resolve(data.length);
  });
});
let p3 = new Promise((resolve, reject) => {
  fs.readFile('./c.txt', 'utf-8', (err, data) => {
    err ? reject(err) : resolve(data.length);
  });
});
p1.then(a => {
  console.log(a);
  return p2;
}).then(b => {
  console.log(b);  	// 接收上面传递的值p2
  return p3;
}).then(c => {
  console.log(c)   	// 接收上面传递的值p3
}).catch((err) => {
  console.log(err);   // 如果错误,打印报错信息
});

catch方法可以统一获取到 错误信息

封装按顺序异步读取文件的函数

function myReadFile(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
            err ? reject(err) : resolve(data.length);
        })
    });
}
myReadFile('./a.txt')
.then(a => {
  console.log(a);
  return myReadFile('./b.txt');
})
.then(b => {
  console.log(b);
  return myReadFile('./c.txt');
})
.then(c => {
  console.log(c)
})
.catch((err) => {
  console.log(err);
});

使用第三方模块读取文件

// then-fs 模块是第三方模块,需要 npm install then-fs  下载安装的
const fs = require('then-fs');
// then-fs 对内置的fs模块进行了重写的封装。调用方法后,返回Promise对象
let p1 = fs.readFile('./files/a.txt', 'utf-8');  // 
let p2 = fs.readFile('./files/b.txt', 'utf-8');
let p3 = fs.readFile('./files/c.txt', 'utf-8');
// 通过then获取结果
p1.then(res => {
  console.log(res.length);
  return p2;
}).then(res => {
  console.log(res.length);
  return p3;
}).then(res => {
  console.log(res.length);
})

async和await 修饰符

ES6 — ES2015

async 和 await 是 ES2017 中提出来的。

异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题。

从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底。它们都有额外的复杂性,都需要理解抽象的底层运行机制。

异步I/O不就是读取一个文件吗,干嘛要搞得这么复杂?异步编程的最高境界,就是根本不用关心它是不是异步。

async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。

ES2017 提供了async和await关键字。await和async关键词能够将异步请求的结果以返回值的方式返回给我们。

async 用于修饰一个 function

await 只能出现在 async 函数内

对于错误处理,可以选择在async函数后面使用 .catch() 或 在promise对象后使用 .catch()

const fs = require('fs');
// 将异步读取文件的代码封装
function myReadFile (path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
            err ? reject(err) : resolve(data.length);
        });
    }).catch(err => {
        console.log(err);
    });
}
async function abc () {
    let a = await myReadFile('./a.txt');
    let b = await myReadFile('./b.txt');
    let c = await myReadFile('./c.txt');
    console.log(b);
    console.log(a);
    console.log(c);
}
abc();

错误处理

前提是得到几个Promise对象,代码如下:

let fs = require('fs');
let p1 = new Promise('./files/a.txt','utf-8');
let p2 = new Promise('./files/bbb.txt','utf-8');  // 注意:这里故意写错路径
let p3 = new Promise('./files/c.txt','utf-8');

获取Promise的结果,可以通过then获取。也可以通过async和await获取。

如果使用then获取结果,那么错误如何处理?在链式调用的尾端,加一个catch方法即可

// ----------------------------通过 then 获取结果-----------------------------
p1.then(res=>{
    console.log(res);
    return p2;
}).then(res=>{
    console.log(res);
    return p3
}).catch(err=>{
    console.log(err);
})

如何使用async和await获取结果,最好的错误处理方案,就是使用 try...catch...

// ---------------------------通过 async/await 获取结果--------------------
async function abc(){
    try{  // 尝试做一些事情
        let r1 = await p1;  // 正常获取结果
        let r2 = await p2;  // 这里出错了,就会抛出错误 throw err.
        let r3 = await p3;
        console.log(r1,r2,r3)
    }catch(e){
        console.log(e);  // catch这里,会抓住前面try里面抛出的错误
    }
}
abc();

小结

Promise是异步任务的一种解决方案

得到Promise对象

获取结果

加载全部内容

相关教程
猜你喜欢
用户评论