Promise知识点

一、前言:为什么会出现Promise?

Promise的重要性我认为没有必要多讲,概括起来说就是五个字:必!须!得!掌!握!

而且还要掌握透彻,在实际的使用中,有非常多的应用场景我们不能立即知道应该如何继续往下执行。

最常见的一个场景就是ajax请求,通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,

这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去。

let xhr = new XMLHttpRequest();
xhr.open('get', 'https://v0.yiketianqi.com/api?unescape=1&version=v61&appid=82294778&appsecret=4PKVFula&city=%E5%8C%97%E4%BA%AC');
xhr.send();
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
            console.log(xhr.responseText)
        }
    }
}

在ajax的原生实现中,利用了onreadystatechange事件,当该事件触发并且符合一定条件时,才能拿到想要的数

据,之后才能开始处理数据,这样做看上去并没有什么麻烦,但如果这个时候,我们还需要另外一个ajax请求,这

个新ajax请求的其中一个参数,得从上一个ajax请求中获取,这个时候我们就不得不等待上一个接口请求完成之

后,再请求后一个接口。

let xhr = new XMLHttpRequest();
xhr.open('get', 'https://v0.yiketianqi.com/api?unescape=1&version=v61&appid=82294778&appsecret=4PKVFula&city=%E5%8C%97%E4%BA%AC');
xhr.send();
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
            console.log(xhr.responseText)
            
            //伪代码....
            let xhr = new XMLHttpRequest();
            xhr.open('get','http://www.xx.com?a'+xhr.responseText);
            xhr.send();
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status>=200 && xhr.status<300){
                        console.log(xhr.responseText)
                        
                    }
                }
            }
        }
    }
}

当出现第三个ajax(甚至更多)仍然依赖上一个请求时,我们的代码就变成了一场灾难。

这场灾难,往往也被称为回调地狱

因此我们需要一个叫做Promise的东西,来解决这个问题,当然,除了回调地狱之外,还有个非常重要的需求就是

为了代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来

上面的写法,是完全没有区分开,当数据变得复杂时,也许我们自己都无法轻松维护自己的代码了。

这也是模块化过程中,必须要掌握的一个重要技能,请一定重视。

二、Promise是什么?

Promise是异步编程的一种解决方案,比传统的解决方案回调函数更合理、更强大。

ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

指定回调函数的方式也变得更加灵活易懂,也解决了异步回调地狱的问题

旧方案是单纯使用回调函数,常见的异步操作有:定时器、fs模块、ajax、数据库操作

从语法上说,Promise是一个构造函数;

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

2.1 Promise的初体验

创建promise对象(pending状态)

const p = new Promise(executor);

其中:

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

resolve函数: 内部定义成功时我们调用的函数 value => {}

reject函数: 内部定义失败时我们调用的函数 reason => {}

executor会在Promise内部立即同步调用,异步操作在执行器中执行

实例对象调用Promise原型中的then方法来完成对结果的处理

<script>
    const p = new Promise((resolve, reject) => {
        //如果咱们公司今年挣钱了,年底就发奖金,否则不发
        resolve('ok');
    })
    console.log(p)
    p.then(() => {
        console.log('发奖金')
    }, () => {
        console.log('不发奖金')
    })
</script>

三、使用Promise的好处?

3.1 指定回调函数的方式更加灵活
  1. 旧的:必须在启动异步任务前指定

  2. promise:启动异步任务->返回promise对象->给promise对象绑定回调函数

    (甚至可以在异步任务结束后指定/多个)

3.2 可以解决回调地狱问题,支持链式调用
  1. 什么是回调地狱?

    回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

  2. 回调地狱的缺点?

    不便于阅读

    不便于异常处理

  3. 解决方案?

    promise链式调用

  4. 终极解决方案?

    async/await

四、Promise实例对象的两个属性

  • PromiseState

    此属性为promise对象的状态属性。

    • fulfilled:成功的状态
    • rejected:失败的状态
    • pending:初始化的状态

    【注】状态只能由pending->fulfilled 或者是 pending->rejected

  • PromiseResult

    此属性为promise对象的结果值(resolve以及reject函数的形参值)

五、resolve函数以及reject函数

  • resolve:修改promise对象的状态,由pending修改到fulfilled;将实参设置到这个属性PromiseResult中。
  • reject:修改promise对象的状态,由pending修改到rejected;将实参设置到这个属性PromiseResult中。

案例1:利用promise来进行读取文件操作

//1.普通文件读取方式
const fs = require('fs');

//2.直接利用readfile来进行读取
/* fs.readFile(__dirname + '/data.txt',(err,data)=>{
    if(err) throw err;
    console.log(data.toString());
}) */

//3.利用promise来实现文件的读取
const p = new Promise((resolve, reject) => {
    fs.readFile(__dirname + '/data.txt', (err, data) => {
        if (err) {
            reject(err);
        }else{
            resolve(data);
        }
    })
}); 

p.then(value=>{
    console.log(value.toString());
},reason=>{
    console.log(reason);
})

案例2:利用promise进行ajax请求

<body>
    <button>发送ajax请求</button>
    <script>
        //1.获取DOM元素对象
        let btn = document.querySelector('button');
        //2.绑定事件
        btn.onclick = function(){
            //3.创建promise实例对象
            const p = new Promise((resolve,reject)=>{
                //4.创建ajax实例对象
                const xhr = new XMLHttpRequest();
                //5.打开请求
                xhr.open('get','https://www.yiketianqi.com/free/day?appid=82294778&appsecret=4PKVFula&unescape=1');
                //6.发送请求
                xhr.send();
                //7.利用onreadystatechange事件
                xhr.onreadystatechange = function(){
                    //8.判断
                    if(xhr.readyState == 4){
                        if(xhr.status == 200){
                            resolve(xhr.responseText);
                        }else{
                            reject(xhr.response);
                        }
                    }
                }
            });
            p.then(value=>{
                console.log(JSON.parse(value));
            },reason=>{
                console.log('获取信息失败');
            })
        }
    </script>
</body>

案例3:利用promise进行数据库操作

const mongoose = require('mongoose');

new Promise((resolve, reject) => {
    mongoose.connect('mongodb://127.0.0.1/project');
    mongoose.connection.on('open', ()=>{
        //连接成功的情况
        resolve();
    });

    mongoose.connection.on('error', () => {
        //连接失败的情况
        reject();
    })
}).then(value => {
    //创建结构
    const NoteSchema = new mongoose.Schema({
        title: String,
        content: String
    })
    //创建模型
    const NoteModel = mongoose.model('notes', NoteSchema);

    //读取操作
    NoteModel.find().then(value => {
        console.log(value);
    }, reason => {
        console.log(reason);
    })
}, reason => {
    console.log('连接失败');
})

案例4:封装一个函数,作用是读取文件

const fs = require('fs');

function ReadFileFun(path){
    return new Promise((resolve,reject)=>{
         fs.readFile(path,(err,data)=>{
              //判断
              if(err){
                    reject(err)
              }else{
                    resolve(data);
              }
         })
    });
}

ReadFileFun('./data.txt').then(value=>{
    console.log(value.toString());
},reason=>{
    console.log(reason);
})

node中的promisify

  • promisify (只能在 NodeJS 环境中使用)
  • promisify 是 util 模块中的一个方法 util 是 nodeJS 的内置模块
  • 作用: 返回一个新的函数, 函数的是 promise 风格的.
const util = require('util');
const fs = require('fs');
//通过 fs.readFile 创建一个新的函数
const mineReadFile = util.promisify(fs.readFile);

mineReadFile('./resource/2.html')
.then(value => {
    console.log(value.toString());
}, reason => {
    console.log(reason);
})

六、Promise对象的状态

Promise对象通过自身的状态来控制异步操作,Promise实例具有三种状态.

  • 异步操作未完成:pending
  • 异步操作成功:fulfilled
  • 异步操作失败:rejected

这三种的状态的变化途径只有两种

  • 从pending(未完成)到fulfilled(成功)
  • 从pending(未成功)到rejected(失败)

一旦状态发生变化,就凝固了,不会再有新的状态变化,这也是Promise这个名字的由来,它的英语意思"承诺",

一旦承诺生效,就不得再改变了,这也意味着Promise实例的状态变化只可能发生一次。

在Promise对象的构造函数中,将一个函数作为第一个参数。而这个函数,就是用来处理Promise的状态变化。

上面的resolve和reject都为一个函数,他们的作用分别是将状态修改为resolved和rejected。

因此,Promise的最终结果只有两种。

异步操作成功,Promise实例传回一个值(value),状态变为fulfilled.
异步操作失败,Promise实例抛出一个错误(error),状态变为rejected

七、Promise的then方法

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

  • 成功的状态:执行第一个回调函数
  • 失败的状态:执行第二个回调函数

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

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

(2) 详细表达:

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

const p = new Promise((resolve,reject)=>{
     resolve('ok');
});

let result = p.then(value=>{
	throw '错误';
},reason=>{
	console.log(reason);
});

console.log(result);

② 如果返回的是非promise的任意值, 新promise变为fulfilled, PromiseResult为返回的值

const p = new Promise((resolve,reject)=>{
                resolve('ok');
});

let result = p.then(value=>{
	return 100;
},reason=>{
	console.log(reason);
});

console.log(result);

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

const p = new Promise((resolve,reject)=>{
                resolve('ok');
});

let result = p.then(value=>{
	return new Promise((resolve,reject)=>{
		//resolve('111');
        reject('error');
	})
},reason=>{
	console.log(reason);
});

console.log(result);

八、Promise的链式调用

const p = new Promise((resolve,reject)=>{
	//resolve('ok');
    reject('error');
});

p.then(value=>{
	console.log(value);
},reason=>{
	console.log(reason);
}).then(value=>{
	console.log(value);
},reason=>{
	console.log(reason);
})

案例:通过promise的链式调用来读取文件

回调地狱的方式:

const fs = require('fs');
fs.readFile('./resource/1.html',(err,data1)=>{
    if(err) throw err;
    fs.readFile('./resource/1.html',(err,data2)=>{
    	if(err) throw err;
        fs.readFile('./resource/1.html',(err,data3)=>{
    		if(err) throw err;
            console.log(data1 + data2 + data3);
		})
	})
})

Promise的形式:

需求:读取resource下三个文件内容,并在控制台合并输出

new Promise((resolve,reject)=>{
	fs.readFile('./resource/1.html',(err,data)=>{
		 //如果失败 则修改promise对象状态为失败
        if(err) reject(err);
        //如果成功 则修改promise对象状态为成功
        resolve(data);
	})
}).then(value=>{
    return new Promise((resolve,reject)=>{
        fs.readFile('./resource/2.html',(err,data)=>{
             //失败
            if(err) reject(err);
            //成功
            resolve([value,data]);
        })
	})
}).then(value=>{
    return new Promise((resolve,reject)=>{
        fs.readFile('./resource/3.html',(err,data)=>{
             //失败
            if(err) reject(err);
            value.push(data);
            //成功
            resolve(value);
        })
	})
}).then(value=>{
    console.log(value.join(""));
})

九、Promise下的几种方法

9.1 Promise.resolve()

将一个普通值转化为promise类型的数据

  • 若参数为非promise对象,则返回的结果为成功状态的promise对象
let p1 = Promise.resolve(123);
console.log(p1);
let p2 = Promise.resolve(undefined);
console.log(p2);
  • 若参数为promise对象,参数的状态决定返回结果的状态
let p3 = Promise.resolve(new Promise((resolve,reject)=>{
	resolve('success');
}));
console.log(p3);

let p4 = Promise.resolve(Promise.resolve(Promise.resolve("OK")));
console.log(p4);
9.2 Promise.reject()

返回的结果始终为失败的Promise对象

console.log(Promise.reject(123));
console.log(Promise.reject(Promise.resolve('ok')));
9.3 Promise.catch()

功能是用来指定失败的回调函数

let p = new Promise((resolve,reject)=>{
	//resolve('success');
    reject('error');
});

p.catch(reason=>{
    console.log(reason);
});

//then方法中不是必须传入两个参数,可以只传递成功时的回调函数
//也可以单独使用catch来指定失败的回调函数

//异常(错误)穿透
//当如果有多个需要执行的成功时的回调函数,可以不需要每一次都写失败回调,可以统一最后利用catch
//当如果promise对象的状态为reject的话,会一直向下穿透直到catch方法
p.then(value=>{
    console.log(value);
}).then(value=>{
    console.log(value);
}).catch(reason=>{
    console.log(reason);
})
9.4 Promise.all()

作用:针对于多个Promise的异步任务进行处理

接收的参数:promise数组

返回值:promise对象,状态由promise数组中的对象状态决定

  • 若每个对象状态都为成功,则返回的promise对象状态为成功,

​ 成功的结果值为每个promise对象成功结构值组成的数组

  • 其中一个对象状态为失败,则返回的promise对象状态为失败,

​ 失败的结果值为失败的promise对象的结果值

let p1 = new Promise((resolve, reject) => {
            resolve('ok');
})
let p2 = Promise.resolve('hello');
let p3 = Promise.resolve('oh yeah');
let result = Promise.all([p1, p2, p3])
console.log(result);

当有一个ajax请求,它的参数需要另外2个甚至更多请求都有返回结果之后才能确定,

那么这个时候,就需要用到Promise.all来帮助我们应对这个场景。

Promise.all接收一个Promise对象组成的数组作为参数,

当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。

//ES6中对Promise.all()的理解以及应用场景
//用于将多个Promise实例,包装成一个新的Promise实例
let p1 = new Promise((resolve,reject)=>{
   resolve('成功01');
})
let p2 = new Promise((resolve,reject)=>{
	resolve('成功02');
}).catch(reason=>console.log(reason));
let p3 = new Promise((resolve,reject)=>{
	resolve('成功03');
})
//参数可以不是数组,但必须是iterator接口
let pAll = Promise.all([p1,p2,p3]);
console.log(pAll)
//pAll的状态是由p1,p2,p3来决定,只有当这三个都为成功,pAll才会为成功,反之,但凡其中一个失败结果就是失败
//这个时候第一个失败的实力的返回值会传递给pAll的回调函数,如果作为参数的实例,自己定义了catch方法,那么它一旦为rejected,是不会触碰到pAll中的catch方法
pAll.then(value=>{
	console.log(value);
},reason=>{
	console.log(reason);
})

案例1:模拟请求三个接口中的数据,全部请求成功后获取。

function getUsersList() {
    return new Promise((resolve, reject) => {
        //模拟请求用户列表数据
        setTimeout(() => {
            resolve('用户列表的数据');
        }, 1000);
    })
}
function getBannersList() {
    return new Promise((resolve, reject) => {
        //模拟请求用户列表数据
        setTimeout(() => {
            resolve('轮播图的数据');
        }, 2000);
    })
}
function getVideoList() {
    return new Promise((resolve, reject) => {
        //模拟请求用户列表数据
        setTimeout(() => {
            resolve('视频列表的数据');
        }, 3000);
    })
}
//初始加载的时候
function initLoad() {
    let all = Promise.all([getUsersList(), getBannersList(), getVideoList()]);
    //获取成功请求的结果值
    all.then(value => {
        console.log(value);
    })
}
initLoad();

案例2:修改多文件读取代码

const fs = require('fs');
const util = require('util');
const mywriteFile = util.promisify(fs.readFile);
let one = mywriteFile('./resource/1.html');
let two = mywriteFile('./resource/2.html');
let three = mywriteFile('./resource/3.html');
let result = Promise.all([one,two,three]);
result.then(value=>{
    console.log(value.join(''));
},reason=>{
    console.log(reason);
})
9.5 Promise.race()

Promise.race race 赛跑的意思

参数: promise 数组

返回结果: promise 对象

状态由『最先改变状态的 promise对象』决定

结果值由 『最先改变状态的 promise对象』决定

let p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('ok');
            }, 2000)
});
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('oh hou');
let result = Promise.race([p1, p2, p3]);
console.log(result);

与Promise.all相似的是,Promise.race都是以一个Promise对象组成的数组作为参数。

不同的是,只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就可以调用.then方法了。

而传递给then方法的值也会有所不同。

<script>
    //ES6中Promise.race的用法以及使用场景
    //将多个Promise实例包装成一个新的Promise实例
    let p1 = new Promise((resolve, rejct) => {
        setTimeout(() => {
            resolve('p1成功')
        }, 2000);
    })
    let p2 = new Promise((resolve, rejct) => {
        setTimeout(() => {
            resolve('p2成功');
        }, 1000);
    }, 1000);
    //调用
    const prace = Promise.race([p1, p2]);
    //Promise.race区别于Promise.all:
    //只要是实例中有一个先改变状态,就会把这个实例的返回值传递给prace的回调函数
</script>
//使用场景:请求超时提示
function request() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('请求成功');
        }, 4000);
    })
}
function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('网络不畅,请求超时');
        }, 3000);
    });
}
Promise.race([request(), timeout()]).then(value => {
    console.log(value)
}).catch(reason => {
    console.log(reason)
})
9.6 Promise.allSettled()

Promise.allSettled()方法,用来确定要一组异步操作是否都结束了(不管成功或失败)。

所以,它的名字叫"Settled",包含了"fufilled"和"rejected"两种情况.

<script>
    function ajax(url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open('get', url, true);
        xhr.send();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.responseText);
                } else {
                    reject(xhr.responseText);
                }
            }
        }
    })

}
//类比Promise下的all方法和allSettled
// Promise.all([ajax('http://www.xiongmaoyouxuan.com/api/tabs'),
// ajax('https://m.maizuo.com/gateway?cityId=110100&k=4770248')
// ]).then(value => {
//     console.log(value)
// }).catch(error => {
//     console.log(error);
// })

Promise.allSettled([ajax('http://www.xiongmaoyouxuan.com/api/tabs'),
                    ajax('https://m.maizuo.com/gateway?cityId=110100&k=4770248')
                   ]).then(value => {
    // console.log(value)
    let successList = value.filter(item => item.status === 'fulfilled');
    console.log(successList)

    let errorList = value.filter(item => item.status === 'rejected');
    console.log(errorList)
}).catch(error => {
    console.log(error);
})
</script>
9.7 Promise.any()

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfiilled状态;

如果所有参数实例都变成rejected,包装实例就会变成rejected状态。

Promise.any()跟Promise.race()方法很像,但是有一点不同,

就是Promise.any()不会因为某个Promise变成rejected状态而结束,

必须等到所有参数Promise变成rejected状态才会结束。

<script>
    let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('ok');
        }, 1000)
    })
    let p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('okk');
        }, 2000)
    })
    let p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error');
        }, 3000)
    })
    Promise.any([p1, p2, p3]).then(res => {
        console.log(res)
    }).catch(err => {
        console.log('error')
    })
</script>
9.8 Promise.finally()

finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象变成fufilled还是rejected状态,最终都会被执行。

finally方法中的回调函数是不接受参数的,因为无论前面是fulfilled状态还是rejected状态, 它都是执行。

const p = new Promise((resolve, reject) => {
    // resolve('ok');
    reject('error');
});
p.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
}).finally(() => {
    console.log('finally')
})

十、终止Promise链条

new Promise((resolve, reject) => {
    resolve(111);
}).then(value=>{
    console.log(value);
    console.log(222);
    //
    // return false;
    // throw '出错啦';
    //有且只有一种方式 返回一个pending状态的promise对象
    return new Promise((resolve, reject) => {});
}).then(value => {
    console.log(333);
}).then(value => {
    console.log(444);
}).catch(reason => {
    console.log(reason);
});

十一、几个关键问题

11.1 如何修改 promise 对象状态
<script>
    //如何修改 promise 对象状态
    let p = new Promise((resolve, reject) => {
        //1. resolve
        // resolve('success');
        //2. reject
        // reject('error');
        //3. 抛出错误 异常
        // throw '出问题啦! 你说出这样的话  你没有良心!!';
        // 状态的改变只有一次 
        resolve('ok');
        reject('error');
    });
	console.log(p);
</script>
11.2 指定多个回调执行情况

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

答:会,但是前提是当promise对象的状态改变(fulfilled/rejected)时才会调用

<script>
        let p = new Promise((resolve, reject) => {
            //promise对象是可以多次调用then方法完成多个成功/失败回调函数
            //但是使用的前提是这个promise对象的状态必须要么是fulfilled或者是rejected
            //不能是pending
            resolve('OK');
        });	
		
        //第一次指定回调
        p.then(value => {
            console.log(value);
        }, reason => {
            console.error(reason);
        });

        p.then(value => {
            alert(value);
        }, reason => {
            alert(reason);
        })
</script>
11.3 指定回调与改变状态先后顺序问题

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

  • 都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态在指定回调

    //若执行器函数中是异步任务, 则先指定回调, 然后再改变状态  更为常见
    //若执行器函数中是同步任务, 则先改变状态, 然后再指定回调
    let p = new Promise((resolve, reject) => {
        //同步任务
        //resolve('ok');
        //异步任务
        setTimeout(() => {
            resolve('ok');
        }, 1000);
    });
    
  • 如何先改状态再指定回调?

    • 在执行器中直接调用resolve()/reject()
    let p = new Promise((resolve, reject) => {
    	//resolve('ok');
    	reject('error');
    });
    
    • 延迟更长时间才调用then()
    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('ok');
        }, 1000)
    
    })
    //then方法使用定时器延迟更久的时间
    setTimeout(() => {
        p.then(value => {
            console.log(value);
        }, reason => {
            console.log(reason);
        })
    
    }, 3000)
    
  • 什么时候才能得到数据?

    • 如果是先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
    • 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
11.4 promise.then()返回的新promise的结果状态由什么决定?
  • 简单表达:由then指定的回调函数执行的结果决定
  • 详细表达:
    • 如果抛出异常:新promise对象状态变成rejected,reason为抛出的异常
    • 如果返回的是是非promise的任意值,新promise对象状态变成fulfilled,value为返回的值
    • 如果返回的是另一个新的promise对象,此promise的结果就会称为新promise的结果
const p = new Promise((resolve, reject) => {
    resolve('ok');
})
let result = p.then(value => {
    //return value;
    return new Promise((resolve, reject) => {
        resolve('okk');
    })
}, reason => {
    console.log(reason);
})
console.log(result);
11.5 promise如何串联多个操作任务?
  • promise的then()返回一个新的promise对象,可以写成then()方法的链式调用
  • 通过then()的链式调用串联多个同步/异步任务
<script>
    new Promise((resolve, reject) => {
            console.log(111);
            reject();
    }).then(value => {
            console.log(222);
    }).then(value=> {
            console.log(value);
    }).then(value => {
            console.log(value)
    }, reason => {
            console.error(reason);
    });
</script>
11.6 promise的异常穿透
  • 当使用promise的then链式调用时, 可以在最后指定失败的回调,
  • 前面任何操作出了异常, 都会传到最后失败的回调中处理
new Promise((resolve, reject) => {
    console.log(111);
    reject('error');
}).then(value => {
    console.log(222);
}).then(value => {
    console.log(value);
}).then(value => {
    console.log(value)
}).catch(reason => {
    console.log(reason);
})
11.7 中断promise链
  • 当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
  • 办法: 在回调函数中返回一个pending状态的promise对象
new Promise((resolve, reject) => {
    console.log(111);
    resolve('ok');
}).then(value => {
    console.log(222);
}).then(value => {
    console.log(value);
    return new Promise(() => { })
}).then(value => {
    console.log(value)
}, reason => {
    console.error(reason);
});

十二、async和await

async/await 是ES7提出的基于Promise的解决异步的最终方案。

12.1 async函数

async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。

因此对async函数可以直接then,返回值就是then方法传入的函数。

// async基础语法
async function fun0(){
    console.log(1);
    return 1;
}
fun0().then(val=>{
    console.log(val) // 1,1
})

async function fun1(){
    console.log('Promise');
    return new Promise(function(resolve,reject){
        resolve('Promise')
    })
}
fun1().then(val => {
    console.log(val); // Promise Promise
}
//声明一个async函数
async function main() {
    console.log('async function');
    //情况1:返回非promise对象数据
    return 'hahaha';
    //情况2:返回是promise对象数据
    /* return new Promise((resolve, reject) => {
		// resolve('ok');
		reject('error');
	}) */
    //情况3:抛出异常
    // throw new Error('出错啦!!!');
}
let result = main().then(value => {
    console.log(value);
});
console.log(result);
12.2 await表达式

await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待

await 修饰的如果是Promise对象,可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语

句才会往下执行;如果不是Promise对象:把这个非promise的东西当做await表达式的结果。

注意事项

  • await必须写在async函数中,但是async函数中可以没有await
  • 如果await的promise失败了,就会抛出异常,需要通过try…catch捕获处理
async function fun(){
    let a = await 1;
    let b = await new Promise((resolve,reject)=>{
        setTimeout(function(){
            resolve('setTimeout')
        },3000)
    })
    let c = await function(){
        return 'function'
    }()
    console.log(a,b,c)
}
fun(); // 3秒后输出: 1 "setTimeout" "function"
function log(time){
    setTimeout(function(){
        console.log(time);
        return 1;
    },time)
}
async function fun(){
    let a = await log(1000);
    let b = await log(3000);
    let c = log(2000);
    console.log(a);
    console.log(1)
}
fun(); 
// 立即输出 undefined 1
// 1秒后输出 1000
// 2秒后输出 2000
// 3秒后输出 3000
async function main() {
    //1、如果await右侧为非promise类型数据
    var rs = await 10;
    var rs = await 1 + 1;
    var rs = await "非常6+7";

    //2、如果await右侧为promise成功类型数据
    var rs = await new Promise((resolve, reject) => {
        resolve('success');
    })

    //3、如果await右侧为promise失败类型数据,需要借助于try...catch捕获
    try {
        var rs = await new Promise((resolve, reject) => {
            reject('error');
        })
        } catch (e) {
            console.log(e);
        }
}
main();
// 使用async/await获取成功的结果

// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('获取成功')
        },3000)
    })
}

async function test(){
    let a = await getSomeThing();
    console.log(a)
}
test(); // 3秒后输出:获取成功

案例:async结合await读取文件内容

//1、导包
const fs = require('fs');
const {promisify} = require('util');
//2、将fs.readFile转化成promise风格的函数
const myreadfile = promisify(fs.readFile);
//3、声明async函数
async function main(){
    try{
        //4、读取文件
        let one = await myreadfile('./resource/4.html');
        let two = await myreadfile('./resource/2.html');
        let three = await myreadfile('./resource/3.html');
    //5、拼接读取文件内容
    console.log(one + two + three);
    }catch(e){
        console.log(e);
    }
}
//6、调用main函数
main();

十三、JS中的宏队列与微队列

  • 说明
    • JS中用来存储待执行回调函数的队列包含2个不同特定的列队
    • 宏列队: 用来保存待执行的宏任务(回调), 比如: 定时器回调/DOM事件回调/ajax回调
    • 微列队: 用来保存待执行的微任务(回调), 比如: promise的回调/MutationObserver的回调
    • JS执行时会区别这2个队列
      • JS引擎首先必须先执行所有的初始化同步任务代码
      • 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行
setTimeout(() => {
    console.log(111);
});

new Promise((resolve, reject) => {
    resolve();
    //return ;throw
    reject();
    console.log(222);
}).then(v => {
    console.log(333);
})
console.log(444);

转载自尚硅谷相关内容

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值