文章目录
【前端目录贴】
1. Promise 的理解和使用
1.1 Promise 是什么?
- 理解
- 抽象表达:
- Promise 是一门新的技术(ES6 规范)
- Promise 是 JS 中进行异步编程的
新解决方案
(备注:旧方案是单纯使用回调函数)- 具体表达:
- 从语法上来说: Promise 是一个构造函数
- 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
1.1.1 promise两个重要属性
- promise 的状态改变(PromiseState (
pending/resolved(fullfilled)/rejected 三种情况
))
- pending 变为 resolved
- pending 变为 rejected
说明: 只有这 2 种, 且一个 promise 对象只能改变一次
,成功的结果数据一般称为 value, 失败的结果数据一般称为 reason,结果只能从pending 变为resolves或rejected;
- promise 的结果(PromiseResult),保存着异步任务『成功/失败』的结果(自己测试打印为空,暂不清楚为啥)
1.1.2 promise 的基本流程
1.2 为什么要用 Promise?
1.2.1 指定回调函数的方式更加灵活
- 旧的: 必须在启动异步任务前指定
- promise: 启动异步任务 => 返回 promie 对象 => 给promise 对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
1.2.2 支持链式调用, 可以解决回调地狱问题
回调地狱:
回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
回调地狱的缺点: 不便于阅读、不便于异常处理
解决方案
: promise 链式调用
终极解决方案?
: async/await
1.3 如何使用Promise
1.3.1 基本编码流程
- 基本编码流程
//普通实现方式
$('button').on('click', function () {
setTimeout(function () {
result= funcationRandom(1, 9);
if (result <= 3) {
alert('中奖了');
} else {
alert('没有中奖')
}
},1000);
console.log('抽奖结束')
});
//promise实现方式
$('button').on('click', function () {
// promise实现方式
// resolve 解决 函数类型的数据
// reject 拒绝 函数类型的数据
//1) 创建 promise 对象(pending 状态), 指定执行器函数
var p=new Promise((resolve,reject)=>{
//2) 在执行器函数中启动异步任务
setTimeout(function () {
result= funcationRandom(1, 9);
/**
* 3) 根据结果做不同处理
* 3.1) 如果成功了, 调用 resolve(), 指定成功的 value, 变为 resolved 状
* 3.2) 根据结果做不同处理:如果成功了, 调用 resolve(), 指定成功的 value, 变为 resolved 状态
*/
if (result <= 3) {
resolve(); // 将 promise 对象的状态设置为 『成功』
} else {
reject(); // 将 promise 对象的状态设置为 『失败』
}
},1000);
});
//调用 then 方法
// value 值
// reason 理由
p.then(() => {
alert('中奖了');
},()=>{
alert('没有中奖')
})
console.log('抽奖结束')
});
- 传值
$('button').on('click', function () {
// promise实现方式
// resolve 解决 函数类型的数据
// reject 拒绝 函数类型的数据
var p=new Promise((resolve,reject)=>{
setTimeout(function () {
result= funcationRandom(1, 9);
if (result <= 3) {
resolve(result); // 将 promise 对象的状态设置为 『成功』
} else {
reject(result); // 将 promise 对象的状态设置为 『失败』
}
},1000);
});
//调用 then 方法
// value 值
// reason 理由
p.then(value => {
alert('中奖了,value='+value);
},reason=>{
alert('没有中奖,reason='+reason)
})
console.log('抽奖结束')
});
- fs异步
var fs = require('fs');
//回调函数形式
// fs.readFile('./aa.txt',(err,data) => {
// if (err) {
// throw err
// }
// console.log(data.toString());
// })
//promise形式
var p = new Promise((resolve, reject) => {
fs.readFile('./aa.txt', (err, data) => {
if (err) {
reject(err); // 将 promise 对象的状态设置为 『失败』
}
resolve(data); // 将 promise 对象的状态设置为 『成功』
})
});
//调用 then 方法
// value 值
// reason 理由
p.then(value => {
console.log(value.toString())
}, reason => {
console.log(reason)
});
- ajax异步
var p=new Promise((resolve, reject)=>{
//1.创建对象
var xhr = new XMLHttpRequest();
//2.初始化 设置请求方法和url
xhr.open('GET', 'http://127.0.0.1:3000/home');
//3.发送
xhr.send();
//4.事件绑定,处理服务端返回的结果
/**
* on when 当...时候
* readystate 是xhr对象中的属性, 表示状态0(初始化) 1(open完毕) 2(send完毕) 3(返回部分结果) 4(返回所有结果)
*/
xhr.onreadystatechange = function () {
//共改变4次,触发4次
//判断(服务端返回了所有的结果)
if (this.readyState === 4) {
//判断响应状态码 200 404 403 401 500
//2xx成功
if (this.status >= 200 && this.status < 300) {
resolve(this.response);
} else {
reject(this.status);
}
}
}
})
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
});
1.3.2 util.promisify方法
将接受这种回调函数作为参数的函数,转换为返回promise的函数
//util.promisify方法: 将接受这种回调函数作为参数的函数,转换为返回promise的函数
//引入util模块
var util = require('util');
//引入fs模块
var fs = require('fs');
//返回promise版本函数
var promisify = util.promisify(fs.readFile);
promisify('./aa.txt').then((value) => {
console.log(value.toString())
}, (reason) => {
console.log(reason)
});
1.3.3 手动封装ajax方法
/**
* 封装一个函数 sendAJAX 发送 GET AJAX 请求
* 参数 URL
* 返回结果 Promise 对象
*/
function sendAJAX(url) {
return new Promise((resolve, reject) => {
//1.创建对象
var xhr = new XMLHttpRequest();
//2.初始化 设置请求方法和url
xhr.open('GET', url);
//3.发送
xhr.send();
//4.事件绑定,处理服务端返回的结果
/**
* on when 当...时候
* readystate 是xhr对象中的属性, 表示状态0(初始化) 1(open完毕) 2(send完毕) 3(返回部分结果) 4(返回所有结果)
*/
xhr.onreadystatechange = function () {
//共改变4次,触发4次
//判断(服务端返回了所有的结果)
if (this.readyState === 4) {
//判断响应状态码 200 404 403 401 500
//2xx成功
if (this.status >= 200 && this.status < 300) {
resolve(this.response);
} else {
reject(this.status);
}
}
}
})
}
sendAJAX('http://127.0.0.1:3000/server3').then(value => {
console.log(value)
}, reason => {
console.log(reason)
});
1.3.4 API
- Promise 构造函数: Promise (excutor) {}
- executor 函数: 执行器 (resolve, reject) => {}
- resolve 函数: 内部定义成功时我们调用的函数 value => {}
- reject 函数: 内部定义失败时我们调用的函数 reason => {}
说明: executor 会在 Promise 内部立即同步调用
,异步操作
在执行器中执行
var p = new Promise((resolve, reject) => {
//同步调用
console.log('111');
setTimeout(function () {
console.log('333');
result=1;
if (result <= 3) {
resolve('444'); // 将 promise 对象的状态设置为 『成功』
} else {
reject('error'); // 将 promise 对象的状态设置为 『失败』
}
},500)
});
//异步调用
p.then((value) => {
console.log(value)
},(reason)=>{
console.log(reason)
});
p.catch((reason)=>{
console.log(reason)
})
console.log('222');
//依次输出111 2222 333 444
- Promise.prototype.then 方法: (onResolved, onRejected) => {}
- onResolved 函数: 成功的回调函数 (value) => {}
- onRejected 函数: 失败的回调函数 (reason) => {}
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象
//异步调用
var pp=p.then((value) => {
console.log(value)
},(reason)=>{
console.log(reason)
});
console.log(pp);//返回一个新的promise 对象
- Promise.prototype.catch 方法: (onRejected) => {}
- onRejected 函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
var ppp=p.catch((reason)=>{
console.log(reason)
})
console.log(ppp);//返回一个新的promise 对象
- Promise.resolve 方法: (value) => {}
- value: 成功的数据或 promise 对象
说明: 返回一个成功/失败的 promise 对象 (结果是传入的值或者 传入的 promise对象的value或reason值
)
/**
* 如果传入的参数为 非Promise类型的对象 -> 则返回的结果为成功promise对象
* 如果传入的参数为 Promise 对象 -> 则参数的结果决定了 resolve 的结果
*/
var p_resolve1=Promise.resolve(123);
var p_resolve2=Promise.resolve(p);
console.log(p)
console.log(p_resolve1)
console.log(p_resolve2)
p_resolve2.then((value) => {
console.log('p_resolve2'+value)
},(reason)=>{
console.log('p_resolve2'+reason)
});
console.log(p===p_resolve2)//true
- Promise.reject 方法: (reason) => {}
- reason: 失败的原因
说明: 返回一个失败的 promise 对象 (失败状态,结果是 输入的字符串值,或者promise对象
)
var p_reject1 = Promise.reject(123);
var p_reject2 = Promise.reject(p);
console.log(p_reject1)//失败状态,结果123
console.log(p_reject2)//失败状态,结果是p
p_reject1.then((value) => {
console.log('p_reject1-then ' + value)
}, (reason) => {
console.log('p_reject1-then ' + reason)
})
p_reject2.then((value) => {
console.log('p_reject2-then ' + value)
}, (reason) => {
console.log('p_reject2-then ' + reason)
});
- Promise.all 方法: (promises) => {}
- promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功(结果:所有成功的promise数组
), 只要有一个失败了就直接失败(结果返回失败的reason值
)
var p_all1=Promise.all([p,p2]);
console.log(p_all1);
- Promise.race 方法: (promises) => {}
- promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态(结果是value或reason值
)
var p_race=Promise.race([p,p2]);
console.log(p_race);
1.3.5 promise 的几个关键问题
- 如何改变 promise 的状态?
var p = new Promise((resolve, reject) => {
//1. resolve 函数
// resolve('ok'); // pending => fulfilled (resolved)
//2. reject 函数
// reject("error");// pending => rejected
//3. 抛出错误
throw '出问题了';
});
console.log(p)
- 一个 promise 指定多个成功/失败回调函数, 都会调用吗?
当 promise 改变为对应状态时都会调用
- 改变 promise 状态和
指定
回调函数谁先谁后?(指定和执行有差别
)
- 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
- 如何先改状态再指定回调?
- 在执行器中直接调用 resolve()/reject()
- 延迟更长时间才调用 then()
- 什么时候才能得到数据?
- 如果先
指定
的回调, 那当状态发生改变时, 回调函数就会调用
, 得到数据
2.如果先改变的状态, 那当指定
回调时, 回调函数就会调用
, 得到数据
var p = new Promise((resolve, reject) => {
// resolve('ok'); // 先改状态再指定回调
setTimeout(() => {
resolve('OK');// 先指定回调再改状态再
}, 3000);
});
console.log(p);
p.then(value => {
console.log(value);
})
// setTimeout(function () {//将then延长更长时间执行
// p.then(value => {
// console.log(value);
// }),5000
// });
- promise.then()返回的新 promise 的结果状态由什么决定?
- 简单表达: 由 then()指定的回调函数执行的结果决定
- 详细表达:
- 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
- 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value
- 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111');// 先指定回调再改状态再
}, 1000);
});
pp = p.then(value => {
console.log(value);
// throw 'error';//1.抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
// return 'ok2';//2.返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('222');// 先指定回调再改状态再
}, 1000);
});//3.返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
})
console.log(p);
console.log(pp);
- promise 如何串连多个操作任务?
- promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用
- 通过 then 的链式调用串连多个同步/异步任务
- promise 异常传透?
- 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调,
- 前面任何操作出了异常, 都会传到最后失败的回调中处理
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111');// 先指定回调再改状态再
// reject('err');// 先指定回调再改状态再
}, 1000);
});
p.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('222');
reject('err');
}, 1000);
});
}).then(value => {
console.log(value);
}).catch(reason => {
console.log(reason)
})//串联(末尾也可用then)
//.catch(value => {
// console.log(value);
// },reason => {
// console.log(reason)
// })
- 中断 promise 链?
- 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
- 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111');// 先指定回调再改状态再
// reject('err');// 先指定回调再改状态再
}, 1000);
});
p.then(value => {
console.log(value);
return new Promise(() => {});//返回pending状态promise对象
}).then(value => {
console.log(value);
}).catch(reason => {
console.log(reason)
})
1.4 手写Promise
1.5 async 与 await
1.5.1 async 函数
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定
(和then一样)
//then
async function main() {
//1. 如果返回值是一个非Promise类型的数据
// return 521;
//2. 如果返回的是一个Promise对象
// return new Promise((resolve, reject) => {
// // resolve('OK');
// reject('Error');
// });
//3. 抛出异常
throw "Oh NO";
}
let result = main();
console.log(result)
result.then(value => {
console.log(value)
},reason=>{
console.log(reason);
});
1.5.2 await 表达式
- await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
- 如果表达式是 promise 对象, await 返回的是 promise 成功的值
- 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
- 如果表达式是其它值, 直接将此值作为 await 的返回值
await 必须写在 async 函数中, 但 async 函数中可以没有 await
1.5.3 async和await结合
var fs = require('fs');
//引入util模块
var util = require('util');
// //返回promise版本函数
var promisify = util.promisify(fs.readFile);
async function main() {
try{
data1=await promisify('./a.txt');
data2=await promisify('./b.txt');
data3=await promisify('./c.txt');
console.log(data1.toString())
console.log(data2.toString())
console.log(data3.toString())
}catch (e) {
console.log(e)
}
}
main();
1.5.4 async和await结合-ajax
/**
* 封装一个函数 sendAJAX 发送 GET AJAX 请求
* 参数 URL
* 返回结果 Promise 对象
*/
function sendAJAX(url) {
return new Promise((resolve, reject) => {
//1.创建对象
var xhr = new XMLHttpRequest();
//2.初始化 设置请求方法和url
xhr.open('GET', url);
//3.发送
xhr.send();
//4.事件绑定,处理服务端返回的结果
/**
* on when 当...时候
* readystate 是xhr对象中的属性, 表示状态0(初始化) 1(open完毕) 2(send完毕) 3(返回部分结果) 4(返回所有结果)
*/
xhr.onreadystatechange = function () {
//共改变4次,触发4次
//判断(服务端返回了所有的结果)
if (this.readyState === 4) {
//判断响应状态码 200 404 403 401 500
//2xx成功
if (this.status >= 200 && this.status < 300) {
resolve(this.response);
} else {
reject(this.status);
}
}
}
})
}
$('#btn').on('click', async function () {
data=await sendAJAX('http://127.0.0.1:8000/server3');
console.log(data);
});