概念
Promise是JS 中进行异步编程的新解决方案(旧的解决方案是回调函数),遵循es6规范。其最大特点就是解决了地狱回调问题。
从语法上来说: Promise是一个构造函数
从功能上来说: promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
异步编程包括
fs 文件操作
require('fs').readFile('./index.html', (err,data)=>{})
数据库操作
ajax
$.get('/server', (data)=>{})
定时器
setTimeout(()=>{}, 2000);
优点
指定回调函数的方式更加灵活
- 旧的: 必须在启动异步任务前指定
- promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函 数(甚至可以在异步任务结束后指定/多个)
支持链式调用,可以解决回调地狱问题
什么是回调地狱? 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
用法
基本写法
//promise构造函数的参数也是一个函数,接收两个参数,这两个参数也是函数
const p = new Promise((resolve, reject) => {
//处理业务
...
//处理业务状态的结果
if(res.status){
resolve(res.response) //如果成功了, 调用 resolve(), 指定成功的 value, 变为 resolved 状态
}else{
reject(res.status) //如果失败了, 调用 reject(), 指定失败的 reason, 变为rejected 状态
}
});
p.then(
res => { // 成功的回调函数 onResolved, 得到成功的 vlaue
...
},
reason => { //失败的回调函数 onRejected, 得到失败的 reason
...
}
)
封装
function xxxxx(params) {
//创建 promise 对象(pending 状态)
return new Promise((resolve, reject) => {
//处理业务
...
//处理业务状态的结果
if(res.status){
resolve(res.response) //如果成功了, 调用 resolve(), 指定成功的 value, 变为 resolved 状态
}else{
reject(res.status) //如果失败了, 调用 reject(), 指定失败的 reason, 变为rejected 状态
}
});
}
//能 promise 指定成功或失败的回调函数来获取成功的 vlaue 或失败的 reason
xxxxx(params).then(
res => { // 成功的回调函数 onResolved, 得到成功的 vlaue
...
},
reason => { //失败的回调函数 onRejected, 得到失败的 reason
...
}
)
nodejs中有一个util.promisify
方法可以自动帮我们封装promise业务。
promise状态
实例对象中的一个属性 『PromiseState』
- pending 未决定的
- resolved / fullfilled 成功
- rejected 失败
Promise 对象的值
实例对象中的另一个属性 『PromiseResult』
保存着异步任务『成功/失败』的结果
- resolve
- reject
执行流程
Promise的Api
Promise 构造函数: Promise (excutor){}
(1) executor 函数: 执行器 (resolve, reject) => {}
(2) resolve 函数: 内部定义成功时我们调用的函数 value => {}
(3) reject 函数: 内部定义失败时我们调用的函数 reason => {}
说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
Promise.prototype.then 方法: (onResolved, onRejected) => {}
(1) onResolved 函数: 成功的回调函数 (value) => {}
(2) onRejected 函数: 失败的回调函数(reason) => {}
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调 返回一个新的 promise 对象
Promise.prototype.catch 方法: (onRejected) => {}
(1) onRejected 函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
Promise.resolve 方法: (value) => {}
(1) value: 成功的数据或 promise 对象
说明: 返回一个成功/失败的 promise 对象
//如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象,状态也是成功的
let p1 = Promise.resolve(521);
//如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('OK'); // 返回 resolved / fullfilled 状态
reject('Error'); // 返回 reject 状态
}));
// console.log(p2);
p2.catch(reason => {
console.log(reason);
})
Promise.reject 方法: (reason) => {}
(1) reason: 失败的原因
说明: 返回一个一直是失败的 promise 对象 (这个对象为reject传入的参数,参数是啥,结果就是啥,状态都是失败的)
Promise.all 方法: (promises) => {}
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise对象。只有传入的所有的 promise 都成功,状态才是成功,返回的是传入的所有成功的promise对象。只要有一个失败了返回的promise状态就是失败,结果为传入的失败的promise对象。
Promise.race 方法: (promises) => {}
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise对象, 第一个完成的 promise 的结果状态就是最终的结果状态。这里完成意思是传入n 个 promise 的数组, 哪一个优先成为promise对象,哪一个就决定了返回对象的结果和状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.race([p1, p2, p3]);//因为p1 1秒后才执行,所以返回的结果为p2,状态也由p2决定
console.log(result);
关键的问题
如何改变 promise 的状态?
const p = new Promise((resolve, reject) => {
//resolve(value);//如果当前是 pending 就会变为 resolved
//reject(reason); //如果当前是 pending 就会变为 rejected
throw "sss"; //如果当前是 pending 就会变为 rejected, 值为"sss"
})
console.log(p1)
一个 promise 指定多个成功/失败回调函数, 都会调用吗?
当 promise 改变为对应状态时都会调用
改变 promise 状态和指定回调函数谁先执行谁后执行?
两种都有可能。如何先改状态再指定回调?
- 在执行器中(excutor)直接调用 resolve()/reject(),然后才调用回调
- 延迟更长时间调用 then()。比如执行器中延时1秒执行,then中延时两秒。
什么时候then先执行?
- 执行器调用resolve()/reject()由延时,或者比then延时更长。就会调用then方法。
如果then先执行什么时候才能得到数据?
- 只有当执行器调用resolve()/reject()完毕之后,then方法的回调(作为then方法的那两个参数)才会执行
promise.then()返回的新 promise 的结果状态由什么决定?
调用then方法,then方法的逐回结果是 Promise对象,对象状态由回调函数的执行结果决定。
- 如果回调函数中返回的结果是非promise 类型的属性,状态为成功,返回值为对象的成功的值.
- 如果回调函数中返回的结果是promise 类型的属性,状态根据then方法内部Promise返回的状态决定。
- 如果直接抛出错误,返回值也是promise类型的,状态是reject,值为抛出错误的值。
//创建实例
const p = new Promise(function (resolve, reject) {
//模拟主体业务代码
setTimeout(() => {
//let data = "成功获取数据库中的数据";
//resolve(data);
let data = "获取失败";
reject(data);
}, 1000);
});
//调用promise实例
const result = p.then(function (value) {
//1.非promise类型的属性
return 'iloveyou';
//2.是 promise对象
return new Promise((resolve, reject) => {
// resolve('ok');
reject('error ');
});
//3.抛出错误
throw new Error("出错啦!");
}, function (reason) {
console.warn(reason);
});
console.log(result);
由于promise返回的是promise类型,所以可以进行链式调用
const fs = require('fs');
const p = new Promise(function (resolve, reject) {
fs.readFile("./source/为学.md", function (err, data) {
if (err) reject(err);
resolve(data);
});
});
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./source/为学1.md", function (err, data) {
resolve([value, data]);
});
});
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./source/为学2.md", function (err, data) {
value.push(data);
return resolve(value);
});
});
}).then(value => {
console.log(value.join('\r\n'));
});
promise 异常传透?
(1) 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调
(2) 前面任何操作出了异常, 都会传到最后失败的回调中处理
p.then(value => {
throw '失败啦!';
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
});
中断 promise 链?
- 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
- 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
自定义封装Promise
function Promise(executor){
this.PromiseState = "pending"
this.PromiseResult = null
this.callback = []
//保存Promise对象到this中
const _this = this;
function resolve(data){
//只能修改一次
if(_this.PromiseState !== "pending") return;
//更改状态和值
_this.PromiseState = "fulfilled"
_this.PromiseResult = data
//如果有保存then方法的参数回调,就执行
if(_this.callback){
_this.callback.forEach(item=>{
item.onResovle(data)
})
}
}
function reject(data){
//只能修改一次
if(_this.PromiseState !== "pending") return;
//更改状态和值
_this.PromiseState = "reject"
_this.PromiseResult = data
//如果有保存then方法的参数回调,就执行
if(_this.callback.onReject){
_this.callback.onReject(data)
}
//如果有保存then方法的参数回调,就执行
if(_this.callback){
_this.callback.forEach(item=>{
item.onReject(data)
})
}
}
try {
//同步调用『执行器函数』
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
Promise.prototype.then = function(onResovle, onReject){
let _this = this
//如果沒有传递onReject参数
if(typeof onReject !== "function"){
onReject = reason => {
throw reason
}
}
//如果沒有传递onResovle参数
if(typeof onResovle !="function"){
onResovle = value => value
}
//返回一个Promise对象
return new Promise((resolve, reject)=>{
function callback(type){
try {
const result = type(_this.PromiseResult)
if(result instanceof Promise){
result.then(v=>{
resolve(v)
}, r=>{
reject(r)
})
}else{
resolve(result)
}
} catch (error) {
reject(error)
}
}
if(this.PromiseState === "fulfilled"){
callback(onResovle)
}
if(this.PromiseState === "reject"){
callback(onReject)
}
if(this.PromiseState === "pending"){
//状态为pending的时候保存回调方法
this.callback.push({
onResovle:function(){
callback(onResovle)
},
onReject:function(){
callback(onReject)
}
})
}
})
}
Promise.prototype.catch = function(onReject){
return this.then(undefined, onReject)
}
Promise.resolve = function(value){
return new Promise((resolve, reject)=>{
if(value instanceof Promise){
value.then(r=>{
resolve(r)
}, v=>{
reject(v)
})
}else{
resolve(value)
}
})
}
Promise.reject = function(value){
return new Promise((undefined, reject)=>{
reject(value)
})
}
Promise.all = function(promise){
return new Promise((resolve, reject)=>{
let count = 0
let arr = []
for(let i=0; i<=promise.length; i++){
promise[i].then(v=>{
count ++;
arr[i] = v;
if(count === promise.length){
resolve(arr)
}
}, r=>{
reject(r);
})
}
})
}
Promise.race = function(promise){
return new Promise((resolve, reject)=>{
for(let i=0; i<=promise.length; i++){
promise[i].then(v=>{
resolve(v)
}, v=>{
reject(v);
})
}
})
}
async和await
文档
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await
async函数
async关键字修饰的函数的返回值为promise对象。
promise对象的结果由async函数执行的返回值决定
函数体内部返回的结果如果是一个非Promise类型的对象,调用函数后返回的结果就是成功Promise对象,promise对象的值为返回的值。
async function fn() {
return 'success';
}
let result = fn();
console.log(result);
如果函数体内抛出错误, 调用函数后返回的结果是一个失败的 Promise。
async function fn() {
throw new Error('出错啦');
}
let result = fn();
console.log(result);
如果函数体内返回是一个Promise对象。
async function fn() {
return new Promise(function (resolve, reject) {
//在Promise对象中如果执行resolve函数,async函数将返回成功的Promise对象,值为resolve传递的参数。
//在Promise对象中如果执行reject函数,函数将返回失败的Promise对象,值为resolve传递的参数
});
}
let result = fn();
console.log(result);
await表达式
await
操作符用于等待一个 Promise
兑现并获取它兑现之后的值。
await
必须写在async
函数中await
右侧的表达式一般为promise
对象await
返回的是promise
成功的值await
的promise
失败了,就会抛出异常,需要通过try...catch
捕获处理。
//创建promise对象
const p = new Promise((resolve, reject) => {
// resolve("用户数据");
reject("失败啦! ");
})
// await要放在async函数中
async function main() {
try {
let result = await p;//await返回的是promise 成功的值
console.log(result);
} catch (e) { //如果调用reject函数,捕获异常
console.log(e);
}
}
//调用函数
main();
async和await结合实践
const fs = require('fs');
function readWeiXue1() {
return new Promise((resolve, reject) => {
fs.readFile("./source/为学.md", function (err, data) {
if (err) reject(err);
resolve(data);
})
});
}
function readWeiXue2() {
return new Promise((resolve, reject) => {
fs.readFile("./source/为学1.md", function (err, data) {
if (err) reject(err);
resolve(data);
})
});
}
function readWeiXue3() {
return new Promise((resolve, reject) => {
fs.readFile('./source/为学2.md', function (err, data) {
if (err) reject(err);
resolve(data);
})
});
}
async function main() {
let wx1 = await readWeiXue1();
let wx2 = await readWeiXue2();
let wx3 = await readWeiXue3();
console.log(wx1.toString());
console.log(wx2.toString());
console.log(wx3.toString());
}
main();
async和await结合ajax实践
function sendAjax(method, url) {
return new Promise((resolve, reject) => {
//创建对象
let xhr = new XMLHttpRequest();
//初始化
xhr.open(method, url);
//发送
xhr.send();
//事件绑定
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status);
}
}
}
})
}
async function main() {
let l1 = await sendAjax("GET", "https://www.tianqi.com/tianqi/mapdata_new.json");
console.log(l1);
}
main();