promise是ES6新提出得用于解决异步编程问题得方法。
1. 同步和异步
1) 同步任务:任务1执行完才能执行任务2,同步任务是直接放在主线程中的。
2)异步任务:异步任务是放在任务队列中的(event queue),只有当同步任务完成后才会执行异步任务。常见的异步任务:事件监听(click等)、回调函数(定时器,ajax请求、nodejs的某些回调),ES6中的promise、generator等,文件操作
2. 事件循环even loop
步骤:
(1) 对所有的任务——判断是否为同步
(2) 如果是同步任务,放到主线程中,按照顺序执行
(3)如果是异步任务,先放到event table中,等该任务的执行条件满足(已经点击了,已经等待2s了),将改任为放到event queue中排队。
(4)当主线程的任务全部完成后,到event queue中检查是否有任务,如果有任务,将其拿到主线程中执行。
3. promise实现异步编程
3.1 promise是什么
抽象的理解:
1) promise是ES新提出的一门技术
2) promise是js中进行异步编程的新解决方案(旧操作:使用回调函数)
具体的表达
1) 从语法上说,promise是一个构造函数
2) 从功能上说,promise对象是用来封装一个异步操作并可以获取成功/失败结果的值。
3.2 为什么使用promise
使用promise的链式调用,可以解决回调地狱问题
1) 回调地狱:回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调函数的条件。
2)回调地狱的缺点:不便于阅读、不便于异常处理
3) 解决回调地狱:promise的链式调用
指定回调函数的方式更加灵活
1)旧的:必须再启动任务之前指定回调函数
2)promise:启动异步任务=》返回promise对象=》给promise绑定回调函数(可以指定多个)
3.3 promise的状态——实例对象的属性
promise对象的三个状态
1) 初始化(等待中)的状态 pending
2) 请求成功的状态 fulfilled/resolved
3) 请求失败的状态 rejected
注意:promise的状态可以由等待中pending,变化为成功或者失败,但是改变为成功或者失败后是不能在改变的,也状态的改变是不可逆的。
3.4 promise对象的值——实例对象的属性(PromiseResult)
保存着异步任务成功/失败的值
resolve和reject可以改变该属性的值。err就是失败的值,data就是成功的值
const p = new Promise(function (resolve, reject) {
fs.readFile(“为学.md”, (err, data) => {
if (err) {//状态为失败,调用失败的回调函数
reject(err);
} else { //状态为成功,调用成功的回调函数
resolve(data.toString());
}
4 promise 的API
4.1Promise构造函数
4.1.1使用步骤
1) 创建promise对象 new Promise(参数) ,状态为等待 //该过程是同步调用的
2) promise对象的参数是一个执行器函数,改函数具有两个参数(回调函数)即new Promise(function(resolve,reject)); reslove回调函数是执行成功时需要调用的回调函数,状态改变为成功,reject是执行失败时要调用的回调函数,状态改编为失败。
3) 通过promise.then()和promise.catch()方法用于捕获promise成功或者失败的状态,并处理返回的结果。
读取文件的异步操作
// promise读取文件
// 1. 引入fs模块
const fs = require("fs");
// 2. 调用方法 err是返回错误,data为读取的数据
// 读取文件就是一个异步操作
const p = new Promise(function (resolve, reject) {
fs.readFile("为学.md", (err, data) => {
if (err) {//状态为失败,调用失败的回调函数
reject(err);
} else { //状态为成功,调用成功的回调函数
resolve(data.toString());
}
});
});
//调用promise对象的then方法
//第一个回调函数函数对应着resolve , 第二个回调函数对应着reject
p.then(
function (value) {
console.log(value);
},
function (reason) {
console.log(reason);
}
);
4.1.2 then()方法和catch()方法——指定回调
两者的区别
promise.then(function(p1){ }, function(p2){ })第一个函数捕获执行成功的状态,第二个函数捕获执行失败的状态。
promise.catch() 是对执行失败结果的处理,相当于,then的第二个函数操作
promise.then方法
- 只有状态改变,才会执行函数
- promise.then()的放回值类型仍是promise
/调用promise对象的then方法
//第一个回调函数函数对应着resolve , 第二个回调函数对应着reject
p.then(
function (value) {
console.log(value);
},
function (reason) {
console.log(reason);
}
);
promise.then() 可以只写一个回调函数
p.then(value=>{
console.log(value);
})
3. 由于promise的返回值仍为promise,所以可以链式回调,避免回调地狱
p.then(value=>{
console.log(value);
}).then(value=>{
console.log(value);
})
promise.catch()方法
用于指定promise失败的回调,相当于的then()方法第二个函数
const fs = require("fs");
// 2. 调用方法 err是返回错误,data为读取的数据
// 读取文件就是一个异步操作
const p = new Promise(function (resolve, reject) {
fs.readFile("为学.md", (err, data) => {
reject(err);
});
});
p.catch(function(value){
log('error')//对应接收reject
})
5 Promise的对象方法
5.1 Promise.resolve方法(value)=>{}
接收一个参数,返回一个成功或者失败的promise对象
1)传入的参数是非promise类型,返回结果均为成功的peomise
2)传入的结果是promise对象,则该参数的结果就决定了resolve的结果
//类型1 参数为非promsie类型
let p1 = Promise.resolve(123);
console.log(p1);
// 类型2 参数为promise类型
let p2 = Promise.resolve(new Promise(function(reslove,reject){
reject('error') //状态为错误,结果值为error
}))
console.log(p2);
类型1返回结果
类型2
5.2 Promise.reject()
接收一个参数,返回的都是失败的Promise对象,即使传入的是一个成功的promise对象,返回的也是失败的promise对象,失败的结果是传入的值。
5.3 Promise.all()方法 (promise)=>{}
(1)传入的参数是包含n个promise的数组
(2) 返回的是一个新的promise,只有所有的promise的状态都成功才返回状态成功,结果值为所有成功的promise成功结果值组成的数组,只要有一个状态失败就直接失败了,失败的结果值是失败的那个promise的结果值。
5.4 promise.race方法 (promises)=>{}
(1)接收的参数promise数组
(2)返回结果,一个新的promise,第一个完成promise的状态就是最终的结果状态(谁先改变状态,就输出谁的结果)
6. promise中的几个关键问题
6.1. 如何改变promise的状态
(1) resolve(value)将pending状态改为resolved
(2)reject(reason) 将pending改为rejected
(3)抛出异常,将pending改为rejected
const p = new Promise(function (resolve, reject) {
// reslove('ok') ; //将pending-》resolved
// reject('error'); //将pending改为rejected
//抛出异常将pending改为rejected
throw '出错了'
});
6.2 一个promise指定多个成功/失败的回调函数,都会调用吗
指定回调函数用then()方法
答案:当promise改变为对应状态时,都会调用
let p =new Promise(function(resolve,reject){
resolve('ok');//将状态改变为成功
});
p.then(function(res){
console.log(res);
})
//再次指定回调
p.then(res=>{
console.log('再次调用');
})
6.3 改变promise的状态和指定调用函数谁先谁后?
也就是resolve()/reject先执行;还是then()
(1)都有可能
正常情况下时先指定回调再改变状态:当执行器中是一个异步任务(居多)
但是也可能先改变状态再指定回调
(2) 如何先改变状态再指定回调
在执行器中直接调用resolve()/reject() //同步任务
延长更长的事件再调用then()
(3)什么时候才得到数据
如果先指定回调,当状态发生改变时,回调函数就会调用,就会得到数据
如果先改变状态,当指定回调时,回调函数就会调用,就会得到数据
6.4 promise.then()返回新promise的结果状态是由什么决定的
由then指定的回调函数执行的结果决定
(1)如果抛出异常,新promise的状态时rejected,返回值reason为抛出异常
(2)如果返回值为非promise的任意值,新promise为resolved, value为返回的值
(3)如果返回的是另一个新的promise,此promise的状态和返回值就变成了新promise的返回结果
6.5 promise如何串联多个操作任务
(1)promise的返回值仍是一个promise对象,可以使用then()的链式调用
(2)通过then的链式调用串联多个同步/异步任务
let p = new Promise((reslove, reject) => {
setTimeout(() => { //异步任务
reslove('ok')
}, 1000);
});
p.then(value => {
console.log(value); //ok
return new Promise((reslove, reject) => {
reslove('success');
})
}).then(value => {
console.log(value); //success
}).then(value => {
console.log(value); //undefined
})
// 最后一个undefine的由来,then的返回值是一个promise对象,
// 但是第二个then没有返回,默认为undefine,即第三个then的输入为undefined
//,undefined不属于promise类型,所以最后返回的状态是成功,值为undefined
6.6 promise的异常渗透
(1)当使用promise的then链式调用时,可以在最后指定失败的回调
(2)前面任何操作出现了异常,都会传到最后的回调中处理
p.then(value => {
console.log(value); //ok
return new Promise((reslove, reject) => {
//reslove('success');
reject('erroe')
})
}).then(value => {
console.log(value); //success
}).then(value => {
console.log(value); //undefined
}).catch(reason=>{
console.log(reason)
//只在最后指定错误的回调函数
//中间的then有错误,也会执行这个回调函数
})
6.7 终端promise链 p.then().then()
(1)当使用promise的then链式调用时,在中间中断,不再调用后边的回调函数
(2)办法: 再回调函数中返回一个pending状态的promise对象
p.then(value => {
console.log(value); //ok
return new Promise((reslove, reject) => {
//reslove('success');
reject('erroe')
})
}).then(value => {
console.log(value); //success
// 中段promise链,一个pending状态的promise对象
reruturn new Promise(()=>{})
}).then(value => {
console.log(value); //undefined
}).catch(reason=>{
console.log(reason)
//只在最后指定错误的回调函数
//中间的then有错误,也会执行这个回调函数
})
7. async函数
(1) 函数的返回值是promise对象
如果返回值是一个非promise类型,结果就是一个成功的promise
如果返回值是一个promise对象,该对象决定了async函数的返回结果
如果抛出异常,返回结果的状态为失败,值为抛出的值
async function main(){
//retrun '521' //第一种类型
return new Promise((resolve,reject)=>{
resolve('ok) //第二种类型
})
//return throw 'error' //第三种类型
}
(2) promise对象的结果由async函数执行的返回值决定
8. await表达式
(1) await的右侧一般为promise对象,也可以是其他的值
(2) 如果表达式时promise对象,await的返回值是promise成功的值
(3)如果表达式是其他值,直接将其值作为await的返回值
(4) await 必须写再async函数中,但async函数中可以没有它
(5) 如果await的promise是失败的,就会抛出异常,try。。catch异常处理
async function main() {
let p = new Promise((resolve, reject)=>{
// resolve('ok');
reject('error');
});
// let res = await p;
// console.log(res);
try {
let res2 = await p;
} catch (e) {
console.log(e);
}
}
笔记来源:
https://www.bilibili.com/video/BV1GA411x7z1?p=45&spm_id_from=pageDriver