ES6异步Promise
Promise
问题:Promise是什么?
是一个异步问题同步化解决方案
问题:Promise存在的意义是什么?
是一个异步问题同步化解决方案,目的为了把异步任务(如ajax请求),实现同步化之后避免会堵塞后面的程序并拿到结果,顺便解决回调地狱
问题:Promise是如何设计的?
设计为Promise执行是同步而promise.then()是异步执行的
问题:为什么Promise执行是同步而promise.then()是异步执行的?
如果promise.then()是同步执行时是不合理的,且会阻塞下面程序的执行
试打印Promise实例对象
console.log(new Promise(function (resolve, reject) { }));/*** Promise {<pending>}* __proto__: Promise* [[PromiseState]]: "pending"* [[PromiseResult]]: undefined*/
Promise实例对象参数是回调函数,名称叫executor执行者
function (resolve, reject) { })
同步执行顺序
new Promise(function (resolve, reject) {console.log('111');})console.log('222');//先打印111,后222
异步操作的特征:
特征一:具有共有的状态,且状态不受外界影响
- 进行中 -
pending - 已成功 -
fullfilled(resolve) - 已失败 -
reject
- 进行中 -
特征二:状态的不可逆性
promise固化以后,再对promise对象添加回调,是可以直接拿到结果的- 如果是事件的话,一旦错过就错过了
同步执行的代码如何异步表现操作呢?
//执行executor函数里第一个参数(resolve)或第二个参数(reject)改变状态为resolve/reject//当执行resolve()/reject()之后,执行绑定的成功/失败后所对应的回调函数//绑定的回调函数可以通过Promise实例对象里的then()方法来写成功/失败后的程序let promise = new Promise(function(resolve,reject){Math.random() * 100 > 60 ? resolve('ok') : reject('no');});//promise.then(注册成功的回调函数,注册失败的回调函数);promise.then((val)=>{console.log(val)}, (err)=>{console.log(err)});
同步/异步执行顺序
let promise = new Promise(function (resolve, reject) {console.log('111');resolve('333');})promise.then((data) => console.log(data))console.log('222');//打印111,222,333//说明执行主线程同步然后其他同步,再执行异步,最后执行定时器
宏任务队列/微任务队列:
在JS异步代码中,存在宏任务队列和微任务队列
- 宏任务:定时器
- 微任务:
resolve/reject的回调函数then()
执行顺序:
同步任务 -> 事件轮巡 -> 主线程全部任务完成 -> 调用任务队列当中的回调函数推入到执行栈中,存在优先权的问题:
- 微任务/微任务中的回调函数先执行(
then()) - 再执行宏任务
//微任务Promise.resolve().then(() => {console.log('promise1');//宏任务setTimeout(() => {console.log('setTimeout2');})})//宏任务setTimeout(() => {console.log('setTimeout1');//微任务Promise.resolve().then(() => {console.log('promise2');})})//打印结果://promise1//setTimeout1//promise2//setTimeout2//执行顺序:这里没有同步任务,只有异步任务//1.微任务优先执行,打印promise1//2.往下执行遇到异步任务setTimeout,挂起到webApis中(等待毫秒)//3.往下执行遇到宏任务setTimeout,执行并打印setTimeout1//4.事件第二轮循环,优先执行微任务打印promise2//5.最后打印setTimeout2
链式调用会存在什么问题?
let promise = new Promise(function (resolve, reject) {resolve('ok');})promise.then((data) => console.log(data)) //ok.then((data2) => console.log('then2:' + data2)) //then2:undefined//说明只能通过第一次then拿到resolve()传入的数据,第二次then却拿不到数据(包括成功/失败)
//解决:手动在第一次then()回调函数里return数据//说明:第一次then的返回值作为下一次then的参数promise.then((data) => {console.log(data);return 'nice';});//ok.then((data2) => console.log('then2:' + data2))//then2:nice
//既然第一次then的返回值作为下一次then的参数//可以将返回值设定为new Promise(),并传入executor回调且调用resolve()传入参数promise//备注:reject()跟resolve()操作一样,不做演示.then((data) => {console.log(data);return new Promise((resolve, reject) => {resolve('第二次then得到的参数');});}) //打印:ok.then((data2) => console.log('then2:' + data2))//打印:then2:第二次then得到的参数
问题1:遇到错误的情况
let promise = new Promise(function (resolve, reject) {resolve(a);})promise.then((val) => console.log(val),(err) => console.log(err))//ReferenceError: a is not defined//哪怕是resolve()传入未定义的变量导致走then()第二个函数打印错误//executor回调函数抛出的错误被then()里的错误函数马上捕获
只捕捉错误的写法
//写法一:promise.then(null, (err) => console.log(err));//写法二:promise.catch((err) => console.log(err));
由上面的写法延申为以下的写法:
//推荐写法:适用于成功/失败的情况promise.then(() => {}).catch(() => {})
问题2:一旦固化,状态不再改变
let promise = new Promise((resolve, reject) => {//状态:成功//注意:执行resolve()意味着固化了状态resolve('success');//未定义变量抛出错误console.log(a);})promise.then((val) => console.log(val)).catch((err) => console.log(err))//打印:success//说明状态固化了只打印resolve的值而不是捕获错误
问题3:链式调用注入数据有可能导致数据丢失
//备注1:不管catch()前面有多少个then(),一旦出现错误都能捕获到(冒泡特性)//备注2:then()不传数据会丢失数据promise.then(() => {}).then().then().catch(() => {})
catch()总结:冒泡特性,状态固定后无法捕获错误
问题4:先执行的固化成功状态后执行reject()会冲突吗?
const p1 = new Promise((resolve, reject) => {setTimeout(() => {//3秒后才执行reject(),然后状态改为失败reject('error');}, 3000)});const p2 = new Promise((resolve, reject) => {setTimeout(() => {//先执行resolve且固化状态为成功//异步p2依赖p1导致状态无效//但是直接把实例对象p1传进来会导致状态无效resolve(p1);}, 1000);});
异步关系管理
Promise.all():管理多个promise实例对象并包装成一个新的实例
Promise.race():管理多个promise实例对象并包装成一个新的实例,并返回第一个返回的数据
//Promise.all()处理多个Promise异步操作且拿到多个Promise所返回的值//以数组或具有iterator数据接口方式传多个值//引入文件模块const fs = require('fs');let p1 = new Promise((resolve, reject) => {fs.readFile('./name.txt', 'utf-8', (err, data) => {//更改为成功状态并把读取到的data传入resolve(data);})})let p2 = new Promise((resolve, reject) => {fs.readFile('./number.txt', 'utf-8', (err, data) => {//更改为成功状态并把读取到的data传入resolve(data);})})let p3 = new Promise((resolve, reject) => {fs.readFile('./score.txt', 'utf-8', (err, data) => {//更改为成功状态并把读取到的data传入resolve(data);})})//拿到成功的结果const p = Promise.all([p1, p2, p3]);console.log(p);//打印一个Promise{}对象p.then((res) => console.log(res));// ['./name.txt','./number.txt','./score.txt']//总结:Promise.all()一旦出现出错,返回第一个出错的报错(不演示)
Promisify
Promise化
通过new Promise方式将异步操作包装一下
function readFile(path) {return new Promise((resolve, reject) => {fs.readFile(path, 'utf-8', (err, data) => {resolve(data);})})}readFile('./name.txt').then(data => readFile(data)).then(data => readFile(data)).then(data => console.log(data));
写一个针对所有异步操作的函数
function promisify(fn) {//收集函数执行的参数return function (...args) {return new Promise((resolve, reject) => {//执行原本的函数,且传入原本的参数以及要处理的最后的回调函数fn(...args, (err, data) => {//判断什么时候resolve/rejectif (err) {reject(err);} else {resolve(data);}})})}}//调用let readFile = promisify(fs.readFile);readFile('./name.txt', 'utf-8').then(data => readFile(data, 'utf-8')).then(data => readFile(data, 'utf-8')).then(data => console.log(data));
将fs模块上的所有方法转为promisify方法
//格式:fs.writeFile => fs.writeFileAsyncfunction promisifyAll(obj) {for (let [key, fn] of Object.entries(obj)) {if (typeof fn === 'function') {obj[key + 'Async'] = promisify(fn);}}}
async/await
适用于异步函数的ES6语法
本质上也是一个语法糖,来源于生成器函数
- 内置的执行器(
co函数) - 更好的语义
- 更广的实用性
async的返回值是一个Promise对象,也有三种状态,每种状态对应每种回调
