1. 什么是Promsie?
Promise 是异步编程的一种解决方案。用于解决传统回调嵌套的问题(回调地狱),多层的回调嵌套使得代码复杂难维护。它由社区最早提出和实现(jq 1.5版本的deferred就已经有了promise的概念),ES6 将其写进了语言标准。
2. Promise基本使用
2.1 最简单的例子
new Promise((resolve, reject) => {
console.log(2) // 2,在new Promise的时候会立即执行
resolve('success')
reject('error')
}).then(res => {
console.log(res) //此时会打印出success
}, reason => { console.log(reason) })
new Promise((resolve, reject) => {
reject('error')
resolve('success')
}).then(res => { console.log(res) }, reason => {
console.log(reason) //此时会打印出error
})
复制代码
从上面一个简单的例子可知:
- Promise是一个类,在new Promise时,需要传入一个executor(执行器函数),这个函数会立即执行
- excutor接收两个参数,参数为两个函数,resolve表示成功, reject表示失败
- Promise默认状态是等待态:pending。可以从pending转换为resolve或是reject。但是不能从resolve或是reject转变成其他状态
- Promise原型上有一个then方法,then方法需要传入两个方法,在Promise对象改变状态的时候调用
2.2 使用Promise处理异步
// 成功的情况
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
})
p.then(res => {
console.log(res) // 100
})
// 失败的情况
let p2 = new Promise((resolve, reject) => {
// 如果的调用了reject,亦或是executor执行函数执行的时候报错
// 都会触发then方法里的第二个函数,并把reject传的值,或是报错的信息传进去
setTimeout(() => {
reject('error')
}, 1000)
})
p2.then(res => {
console.log(res)
}, reason => {
console.log(reason) // error
})
复制代码
2.3 Promsie:链式调用
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
})
p.then(res => {
console.log(res) // 100
return 200 // 如果有返回值,那么会传到写一个then方法的回调里
}).then(res => {
console.log(res) // 200
})
复制代码
- 如果一个then方法,return一个普通值,则这个值会传递到下一个then的成功回调中
- 如果不是普通值—promise 或是 throw error
- 如果是一个promise,会根据这个promise是resolve还是reject,决定下一个then执行成功还是失败的回调
- 捕获错误的机制,(默认会找离自己最近的then的失败的回调,找不到就继续向下找)
3. async和await
既然讲到了Promise是用于处理异步以解决回调地狱的问题。那么就顺带提一下async/await, 这是ES7新增的。以往的异步方法无外乎回调函数和Promise。但是async/await建立于Promise之上
- async: 修饰函数,最后默认让函数返回一个promise实例
- 函数执行报错,实例状态是失败,结果是报错原因,就会走到then方法的失败的回调里
- 如果函数正常执行,则实例状态是成功,结果是return后面的值。return的值会走到then方法的成功回调里
- 一般都是配合await的「函数中使用await,则必须基于async修饰才可以」
async function fn() {
return 10;
}
fn().then(result => {
console.log(result) // 10
})
复制代码
函数中使用await,则必须基于async修饰才可以
- await后面一半都是跟着promise实例
- 如果后面是一个正常的值,如:await 10,会被转成await Promise.resolve(10)
- 如果后面是一个函数,如 await func,
- 首先立即执行func函数,接收它的返回值
- 然后变成await 返回值,最后转成一个promise实例
- 本身是异步微任务:把当前上下文中, await下面要执行的代码整体存储到异步的微任务中,当await后面的promise实例状态为成功后,再去执行下面的代码
- 如果对失败的promise实例没有做异常的处理,则控制台抛出异常信息「不会影响后续代码执行」。那么await需要基于try catch做异常捕获
function computed() {
console.log(1)
return new Promise(resolve => {
setTimeout(() => {
resolve(2)
}, 1000)
})
}
console.log(3)
async function fn() {
console.log(4)
let result = await computed()
// 会先执行computed函数
// 并且等到里面的promise实例状态变为成功后,await下面的代码才会开始执行
console.log(result)
console.log(5)
}
fn()
console.log(6)
// 3, 4, 1, 6, 2, 5
复制代码
4. 自己实现一个Promise吧
上面介绍了一些Promise的基本使用,那么就基于Promise A+规范来实现以下对应的功能
- Promise类
- 处理异步
- 链式调用then
- Promise.resolve,Promise.reject
- Promise.all,Promise.race
- catch,finally
function Promise(executor) {
let self = this
self.status = 'pending' //Promise默认状态为pending
self.value = undefined
self.reason = undefined
self.onResolveCallbacks = [] //存放所有成功的回调--异步逻辑
self.onRejectCallbacks = [] //存放所有失败的回调--异步逻辑
function resolve(value) {
if (self.status === 'pending') { //只能从pending转成其他状态
self.status = 'resolve'
self.value = value
// 当状态发生改变后,执行成功的回调(发布订阅模式)
self.onResolveCallbacks.forEach(fn => fn())
}
}
function reject(reason) {
if (self.status === 'pending') { //只能从pending转成其他状态
self.status = 'reject'
self.reason = reason
// 当状态发生改变后,执行失败的回调(发布订阅模式)
self.onRejectCallbacks.forEach(fn => fn())
}
}
//传递一个executor:执行器(函数),会立即被调用
// 防止在new Promise时,还没调用resolve或是reject前就直接抛出异常
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
//promise直接返回resolve
Promise.resolve = function(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
//promise直接返回reject
Promise.reject = function(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
/*
Promise.all实现思路:
1.如果参数为空数组,则返回一个已完成状态的promise
2.如果数组不为空,且数组每项为promise对象,参数中的 promise 都变成完成状态,
Promise.all 返回的 promise 异步地变为完成。
3.如果传入的参数中其中一个promise失败,则返回reject。不管其他的是否完成
4.在任何情况下。Promise.all的完成状态的结果 都是一个数组
*/
Promise.all = function(values = []) {
return new Promise((resolve, reject) => {
if (values.length === 0) {
resolve([])
return
}
let arr = [] //最后的结果
let index = 0
function processResult(key, value) {
index ++
arr[key] = value
if (index === values.length) {
resolve(arr)
}
}
for (let i = 0; i < values.length; i ++) {
let current = values[i]
if (current && current.then && typeof current.then === 'function') {
current.then(y => {
processResult(i, y)
}, reject)
//如果传入的参数中其中一个promise失败,则返回reject。不管其他的是否完成
} else {
//如果是普通值,直接完成当前的这个promise
processResult(i, current)
}
}
})
}
/*
Promise.race实现思路:
1.如果参数为空数组,则返回一个已完成状态的promise
2.如果数组不为空,且数组每项为promise对象,参数中的 只要有一个 promise 变成完成状态,
Promise.race 就马上返回 已完成promise的值。
3.如果传入的参数中其中一个promise失败,则直接返回reject
*/
Promise.race = function(values) {
return new Promise((resolve, reject) => {
if (values.length === 0) {
resolve()
return
}
for (let i = 0; i < values.length; i ++) {
let current = values[i]
if (current && current.then && typeof current.then === 'function') {
current.then(y => {
resolve(y)
}, reject)
//如果传入的参数中其中一个promise失败,则返回reject。不管其他的是否完成
} else {
//如果是普通值,直接完成当前的这个promise
resolve(current)
}
}
})
}
//onFulfilled和onRejected都是可选的参数
Promise.prototype.then = function(onFulfilled, onRejected) {
//防止then参数不写的情况,可以把then结果传到下一个then
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err}
const self = this
//调用then后,需要再继续返回一个全新的promise(为了实现promise的链式调用)
//因为promise的状态改成resolve或是reject后,就不能再改变了
//所以应该返回的是一个全新的promise
//在返回一个全新的promise前,需要拿到当前这个then中的onFulfilled(成功)或是onRejected(失败)的结果
//判断当前then的执行结果与promise2的关系。执行promise2对应的resolve和reject
let promise2 = new Promise(function(resolve, reject) {
if (self.status === 'resolve') {
setTimeout(() => {
//这里需要到promise2,为了能正确获取到promise2,需要增加异步
try {
const x = onFulfilled(self.value)
//判断当前then方法返回的是否为一个promise
resolvePromsie(promise2, x, resolve, reject)
} catch (e) {
//如果有错误,则抛出错误
//如果onFulfilled或onRejected抛出一个异常e,
//promise2 必须被拒绝(rejected)并把e当作原因
//下面几个try{}catch(){}同理
reject(e)
}
}, 0)
}
if (self.status === 'reject') {
setTimeout(() => {
try {
const x = onFulfilled(self.reason)
//判断当前then方法返回的是否为一个promise
resolvePromsie(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
//订阅异步逻辑,如果是异步,先存起来
if (self.status === 'pending') {
self.onResolveCallbacks.push(function() {
setTimeout(() => {
try {
const x = onFulfilled(self.value)
//判断当前then方法返回的是否为一个promise
resolvePromsie(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
self.onRejectedCallbacks.push(function() {
setTimeout(() => {
try {
const x = onRejected(self.reason)
//判断当前then方法返回的是否为一个promise
resolvePromsie(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return promise2 //返回一个全新的promise,这样就可以在then后再继续调用then方法
}
// catch 方法其实很简单,相当于 then 方法的一个简写
Promise.prototype.catch = function(err){
return this.then(null,err)
}
// 不管Promise最后的状态如何,都要执行一些最后的操作
Promise.prototype.finally = function(onDone){
return this.then(onDone, onDone)
}
function resolvePromsie(promise2, x, resolve, reject) {
if (promise2 === x) {
//如果promise2和x引用同一个对象,则用TypeError作为原因拒绝(reject)
//如果当前的x也是promise2,则直接调用promise2的失败回调reject
return reject(new TypeError('promise 循环调用'))
}
let called //禁止多次调用resolve或是reject,如果多次被调用,值执行第一个,后面的忽略
//判断x是不是一个对象或是函数,如果都不是,则为一个常量,如果是常量,直接传到promise2的成功回调resolve
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
//x不为null,且x是一个对象或是函数。
//如果x是个对象或者方法。取到.then -----判断是不是promise,如果取不到then。则抛出异常
try {
let then = x.then
if (typeof then === 'function') {
//如果then是函数,则表示x是一个promise
//如果是then是一个方法,直接执行---then接收两个参数
then.call(x, y => {
//resolve(y) // 直到y是一个普通值,然后调用resolve
//但是y可能也是一个promise,所以要递归进行判断, 直到y是一个普通值
if (called) return
called = true
resolvePromsie(promise2, y, resolve, reject)
}, r => {
if (called) return
called = true
reject(r)
})
} else {
//如果x下then不是一个函数,则表示x是一个普通对象,直接调用resolve把x返回
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(e)
}
} else {
//如果 x既不是对象也不是函数,也没报错。则直接调用promise2的成功回调resolve
resolve(x)
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END





















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)