promise深入理解和使用
概念
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 = nullthis.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 = 0let 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();