Promise由浅入深
Promise是什么?
1、Promise是ES6提供的进行异步编程的解决方案
2、Promise是一个构造函数,用来封装一个异步操作,可以获取其成功或失败的值
异步编程旧的操作都是回调函数的方式:(不利于阅读和异常处理)
1)fs 文件操作
require('fs').readFile('./index.js', (err, data) => { })
2)ajax
$.get('url', (data) => { })
3)定时器
setTimeout(() => { }, 1000);
读取文件时采用回调的方式和promise的区别:
const util = require('util') const fs = require('fs') // 1、回调函数的写法 fs.readFile('./resources/为学.md', (err, data) => { if (err) throw err console.log(data + '') }) let readFile = util.promisify(fs.readFile) // 将回调函数风格的方法转为promise // 2、promise写法 readFile('./resources/为学.md').then((data) => { console.log(data + '') }) // 3、promise封装读取文件的方法 function getFile(path) { return new Promise((resolve, reject) => { require('fs').readFile(path, (err, data) => { if (err) reject(err) resolve(data) }) }) } getFile('./resources/为学.md') .then((data) => { console.log(data + '') }) .catch((err) => { console.log(err) })
使用promise加载图片:
function loadImage(src) { const promise = new Promise((res, rej) => { const img = document.createElement('img') img.src = src img.onload = function () { res(img) } img.onerror = function () { const error = new Error(`图片加载失败,url为:${src}`) rej(error) } }) return promise } const src1 = 'https://img2020.cnblogs.com/blog/1742906/202105/1742906-20210514231342257-1073217931.jpg' const src2 = 'https://s.yimg.com/cv/apiv2/default/20181030/500x500/celtics_wbg.png' loadImage(src1) .then((res) => { console.log(res) return loadImage(src2) }) .then((res) => { console.log(res) }) .catch((err) => { console.log(err) })
promise的状态:
它的状态是实例对象中的 PromiseState 属性的值,共有3个:
pending 未决定的
resolved/fulfilled 成功
rejected 失败
它的状态变化只有两种可能,一是由pending变为resolved,一是由pending变为rejected,并且一个promise对象的状态只能改变一次,不可能由成功变为失败或由失败变为成功
如何改变promise对象的状态:
1、调用reslove(),将promise的状态由pending变为resolved
2、调用reject(),将promise的状态由pending变为rejected
3、执行throw语句,可以写成throw new Error('出错啦')也可以写成throw ‘出错啦’,将promise的状态由pending变为rejected
promise对象的值:
它的值是实例对象中的 PromiseResult 属性,保存了promise对象成功/失败的结果,没有执行成功或者失败该属性值为undefined
PromiseResult只能由resolve()和reject()改变,保存它们传来的值,在实例对象中用then()接收resolve()的值,用catch()接收reject()的值,或者是用then()方法中两个回调函数,第一个接收resolve()的值,第二个接收reject()的值
API:
Promise(executor){}
执行器函数(executor函数):(resolve,reject)=>{}
resolve函数:内部定义成功时调用的函数 value=>{}
reject函数:内部定义失败时调用的函数 err=>{}
注意:执行器函数会在Promise内部立即同步调用,异步操作在执行器中执行
then()方法
(onResolved,onRejected)=>{}
onResolved函数:成功的函数 (value)=>{}
onRejected函数:失败的函数 (err)=>{}
catch()方法
(onRejected)=>{}
onRejected函数:失败的函数 (err)=>{}
构造函数的方法:
Promise.resolve()
/* 如果传入的参数为非promise类型的对象,则返回的结果为成功的promise对象 如果传入的参数为promise类型的对象,则返回的结果决定了resolve的结果 */ let p = Promise.resolve(111) console.log(p) let p1 = Promise.resolve(new Promise((resolve, reject) => { reject('错误') })) p1.catch(err => { console.error(err) })
Promise.reject()
// 不管传入的是什么,都会返回失败的结果 let p = Promise.reject(123) p.catch(err => { console.log(err) }) console.log(p) // 即使传入的是一个成功的promise对象,返回的也依然是失败的结果,这个结果是成功的promise let p1 = Promise.reject(new Promise((resolve, reject) => { resolve(456) })) p1.catch(err => { console.log(err) }) console.log(p1)
Promise.all()
/* 如果数组中的promise对象都是成功的状态,那么返回的状态也是成功的,返回的结果是这三个参数组成的数组 如果数组中的promise对象有一个是失败的状态,那么返回的状态就是失败的,返回的结果是第一个失败的promise的结果
返回的顺序和传入的顺序保持一致 */ let p = new Promise((resolve, reject) => { resolve('成功') }) let p1 = Promise.resolve('ok') let p2 = Promise.resolve('OK!') const result = Promise.all([p, p1, p2]) console.log(result) // PromiseState 的值为 fulfilled PromiseResult 的值为 ['成功', 'ok', 'OK!']
Promise.all()的应用:
Promise.race()
// race是赛跑的意思,返回的promise对象的状态和结果为数组中第一个执行完的promise的状态和结果 let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('成功') }) }) let p1 = Promise.reject('err') let p2 = Promise.resolve('OK!') let result = Promise.race([p, p1, p2]) console.log(result) // 返回p1的状态和结果
Promise的几个问题:
1、只要promise的状态发生改变,即执行了resolve()或reject()或throw '',它指定的多个成功/失败回调函数都会调用
let p = new Promise((resolve, reject) => { // resolve('成功') // reject('错误') throw '错误' }) p.catch(err => { console.log(err) }) p.catch(err => { console.log(err) })
2、改变promise状态和指定回调函数谁先执行?正常情况下promise中都是异步任务,先执行then()方法再改变promise状态
// 如果promise中是同步任务,那么再改变promise状态再指定回调函数(执行then()方法);如果promise中是异步任务,那么先指定回调函数(先执行then()方法)再改变promise状态,这种情况居多 let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('成功') }, 100) }) p.then(data => { console.log(data) // 此时是先指定回调函数再改变promise状态 }, err => { console.log(err) })
3、如何先改变promise状态再指定回调函数
1)在执行器中直接调用 resolve()/reject()----同步任务
2)延迟更长的时间才调用 then()
4、什么时候能得到数据
1)如果先指定回调,那么当状态发生改变时回调函数就会调用,得到数据----异步任务
2)如果先改变状态,那么当指定回调时回调函数就会调用,得到数据----同步任务
5、promise.then()返回的新promise的状态和结果由什么决定
1)then()方法中有两个回调函数,then()返回的promise的状态和结果由原来的promise指定的回调函数执行的结果决定
2)具体来说:
①如果返回的不是promise对象,那么新promise的状态为fulfilled,结果为返回的值。不写return语句,默认是return undefined
②如果返回的是promise对象,那么新promise的状态和结果为当前promise的状态和结果
③如果抛出异常,新promise的状态为rejected,结果为抛出的值
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('成功') }, 100); }) p.then(data => { return new Promise((resolve, reject) => { resolve('ok') }) }).then(data => { console.log(data) // ok }).then(data => { console.log(data) // undefined 因为上一个then()语句中没有写return,默认是return undefined })
7、异常穿透的特性
// 异常穿透:在promise的执行过程中,如果执行了reject()或者throw语句,只需要在最后的catch()中进行处理,中间不需要返回失败的promise let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('成功') // reject('失败') }, 100); }) p.then(data => { return new Promise((resolve, reject) => { resolve('ok') }) }).then(data => { console.log(data) throw '失败啦' }).then(data => { console.log(data) // return new Promise((resolve, reject) => { // reject('失败了') // }) }).catch(err => { console.log(err) // 失败啦 })
8、中断promise链
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('成功') }, 100); }) p.then(data => { console.log(111) // throw 123 // 会执行catch语句 return new Promise(()=>{}) // 有且只有返回一个pending状态的promise,才会中断promise链 }).then(data => { console.log(222) }).then(data => { console.log(333) }).catch(err => { console.log(err) })
Promise封装
ES5:

function Promise(executor) { this.PromiseState = 'pending' // 实例对象添加状态属性 this.PromiseResult = null // 实例对象添加结果属性 this.callbacks = [] // 提供一个数组用于保存所有的回调函数 const _this = this // 保存实例对象this的值 // 定义resolve函数 function resolve(data) { if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次 _this.PromiseState = 'fulfilled' // 修改对象的状态 _this.PromiseResult = data // 修改对象的结果 // if(_this.callback.onResolved) _this.callback.onResolved(data) // 为什么在这里调用回调函数而不是在then方法中:异步任务先指定了回调,在状态发生改变时执行回调函数 _this.callbacks.forEach((item) => { item.onResolved(data) }) } // 定义reject函数 function reject(data) { if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次 _this.PromiseState = 'rejected' // 修改对象的状态 _this.PromiseResult = data // 修改对象的结果 // if(_this.callback.onRejected) _this.callback.onRejected(data) // 在这里调用回调函数:异步任务先指定了回调,在状态发生改变时执行回调函数 _this.callbacks.forEach((item) => { item.onRejected(data) }) } // try{}catch(){} 语句处理throw抛出的异常 try { executor(resolve, reject) // 同步调用执行器函数 } catch (err) { reject(err) } } Promise.prototype.then = function (onResolved, onRejected) { // 提供异常穿透。如果then()中的第二个参数没有传递,默认抛出onRejected回调 if (typeof onRejected !== 'function') onRejected = (reason) => { throw reason } // 如果第一个参数没有传递,默认执行onResolved()回调 if (typeof onResolved !== 'function') onResolved = (data) => data return new Promise((resolve, reject) => { const callback = (type) => { try { let result = type(this.PromiseResult) if (result instanceof Promise) { result.then( (data) => { resolve(data) }, (err) => { reject(err) } ) } else { resolve(result) } } catch (e) { reject(e) } } if (this.PromiseState === 'fulfilled') { setTimeout(() => { callback(onResolved) }) } else if (this.PromiseState === 'rejected') { setTimeout(() => { callback(onRejected) }) } else if (this.PromiseState === 'pending') { this.callbacks.push({ onResolved: () => { setTimeout(() => { callback(onResolved) }) }, onRejected: () => { setTimeout(() => { callback(onRejected) }) } }) // 保存回调函数 } }) } Promise.prototype.catch = function (onRejected) { return this.then(undefined, onRejected) } Promise.resolve = function (result) { return new Promise((resolve, reject) => { if (result instanceof Promise) { result.then( (data) => { resolve(data) }, (err) => { reject(err) } ) } else { resolve(result) } }) } Promise.reject = function (err) { return new Promise((resolve, reject) => { reject(err) }) } Promise.all = function (promiseArr) { return new Promise((resolve, reject) => { let count = 0, list = [] promiseArr.forEach((item, index) => { item.then( (data) => { count++ list[index] = data if (count === promiseArr.length) resolve(list) }, (err) => { reject(err) } ) }) }) } Promise.race = function (promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach((item) => { item.then( (data) => { resolve(data) }, (err) => { reject(err) } ) }) }) }
ES6:

class Promise { constructor(executor) { this.PromiseState = 'pending' // 实例对象添加状态属性 this.PromiseResult = null // 实例对象添加结果属性 this.callbacks = [] // 提供一个数组用于保存所有的回调函数 const _this = this // 保存实例对象this的值 // 定义resolve函数 function resolve(data) { if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次 _this.PromiseState = 'fulfilled' // 修改对象的状态 _this.PromiseResult = data // 修改对象的结果 // if(_this.callback.onResolved) _this.callback.onResolved(data) // 为什么在这里调用回调函数而不是在then方法中:异步任务先指定了回调,在状态发生改变时执行回调函数 _this.callbacks.forEach((item) => { item.onResolved(data) }) } // 定义reject函数 function reject(data) { if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次 _this.PromiseState = 'rejected' // 修改对象的状态 _this.PromiseResult = data // 修改对象的结果 // if(_this.callback.onRejected) _this.callback.onRejected(data) // 在这里调用回调函数:异步任务先指定了回调,在状态发生改变时执行回调函数 _this.callbacks.forEach((item) => { item.onRejected(data) }) } // try{}catch(){} 语句处理throw抛出的异常 try { executor(resolve, reject) // 同步调用执行器函数 } catch (err) { reject(err) } } then(onResolved, onRejected) { // 提供异常穿透。如果then()中的第二个参数没有传递,默认抛出onRejected回调 if (typeof onRejected !== 'function') onRejected = (reason) => { throw reason } // 如果第一个参数没有传递,默认执行onResolved()回调 if (typeof onResolved !== 'function') onResolved = (data) => data return new Promise((resolve, reject) => { const callback = (type) => { try { let result = type(this.PromiseResult) if (result instanceof Promise) { result.then( (data) => { resolve(data) }, (err) => { reject(err) } ) } else { resolve(result) } } catch (e) { reject(e) } } if (this.PromiseState === 'fulfilled') { setTimeout(() => { callback(onResolved) }) } else if (this.PromiseState === 'rejected') { setTimeout(() => { callback(onRejected) }) } else if (this.PromiseState === 'pending') { this.callbacks.push({ onResolved: () => { setTimeout(() => { callback(onResolved) }) }, onRejected: () => { setTimeout(() => { callback(onRejected) }) } }) // 保存回调函数 } }) } catch(onRejected) { return this.then(undefined, onRejected) } static resolve(result) { return new Promise((resolve, reject) => { if (result instanceof Promise) { result.then( (data) => { resolve(data) }, (err) => { reject(err) } ) } else { resolve(result) } }) } static reject(err) { return new Promise((resolve, reject) => { reject(err) }) } static all(promiseArr) { return new Promise((resolve, reject) => { let count = 0, list = [] promiseArr.forEach((item, index) => { item.then( (data) => { count++ list[index] = data if (count === promiseArr.length) resolve(list) }, (err) => { reject(err) } ) }) }) } static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach((item) => { item.then( (data) => { resolve(data) }, (err) => { reject(err) } ) }) }) } }
async和await:
async函数:函数的返回值为promise对象,promise对象的结果由async函数执行的返回值决定,和then()中的返回规则一样,具体如下:
async function fn() { let p = new Promise((resolve, reject) => { // resolve('ok') reject('err') }) try { let res = await p console.log(res) } catch (e) { console.log(e) } } fn()
async和await结合读取文件(异步代码写的和同步代码一样)和原来的回调函数方式的区别:
const fs = require('fs') const util = require('util') const myReadFile = util.promisify(fs.readFile) // 将回调函数风格的方法转为promise // 回调函数的方式 fs.readFile('./resources/为学.html', (err, data) => { if (err) throw err fs.readFile('./resources/插秧诗.html', (err, data1) => { if (err) throw err fs.readFile('./resources/观书有感.html', (err, data2) => { if (err) throw err console.log(data + data1 + data2) }) }) }) // async和await的方式 async function getFile() { try { let res = await myReadFile('./resources/为学.html') let res1 = await myReadFile('./resources/插秧诗.html') let res2 = await myReadFile('./resources/观书有感.html') console.log(res + res1 + res2) } catch (e) { console.log(e) } } getFile()
async和await在实际项目中应用:
微任务与宏任务:
宏任务:setTimeout、setInterval、DPM事件、ajax
微任务:Promise、async/await
执行顺序:微任务 > DOM > 宏任务
const con = $('<p>一段内容</p>') $('#box').append(con) console.log(1) setTimeout(() => { console.log(2) alert('setTimeout') }, 0); Promise.resolve().then(()=>{ console.log(3) alert('Promise') })
上面代码的dom渲染将在then执行完、定时器开启前渲染
x
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
2019-12-08 事件代理/事件委托----点击li弹出对应的下标和内容
2019-12-08 jQuery在线引用地址
2019-12-08 获取地址栏参数并转化为对象