
8

MS: JS异步编程之Promise
source link: https://lianpf.github.io/posts/%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/05.js%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E4%B9%8Bpromise/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、Promise
的使用
创建一个promise
实例,方便处理异步操作
var p = new Promise(function(resolve,reject){
setTimeout(function(){
resolve("success")
},1000);
console.log("创建一个新的promise");
})
p.then(function(x){
console.log(x)
})
// 输出:创建一个新的promise
// 输出:success
promise.then()
链式调用
var p = new Promise(function(resolve, reject) {
resolve()
});
p.then(...).then(...).then(...)
其他方法:
Promise.resolve()
Promise.all()
Promise.race()
二、Promise原理剖析
1.Promise 凭借什么消灭了回调地狱
2.为什么Promise要引入微任务
3.Promise 如何实现链式调用
正文。。。
三、手写Promise/A+
规范的Promise
Promise/A+
规范:
『术语』:
promise
是一个对象或者函数,该对象或者函数有一个then
方法thenable
是一个对象或者函数,用来定义then
方法value
是promise
状态成功时的值reason
是promise
状态失败时的值
promise
必须有3个状态,pending
、fulfilled(resolved)
和rejected
- 当处于
pending
状态的时候,可以转移到fulfilled(resolved)
或rejected
状态 - 当处于
fulfilled(resolved)
状态或rejected
状态的时候,则不可变
- 当处于
- 一个
promise
必须有一个then
方法then
方法接受两个参数:promise.then(onFulfilled, onRejected)
onFulfilled
方法表示状态从pending——>fulfilled(resolved)
时所执行的方法onRejected
表示状态从pending——>rejected
所执行的方法。
- 为了实现链式调用,then方法必须返回一个promise。
promise2=promise1.then(onFulfilled,onRejected)
1.Promise
基础结构
- 执行回调数组:当
resolve()
延迟执行时,Promise
的状态为pending
,不应立即执行成功调用的函数,需要把它存起来,直到执行resolve
再执行成功调用的函数 - 链式调用:
Promise
里的链接调用是返回一个新的Promise
对象 - 异步执行:用
setTimeout
模拟微任务(JS事件循环执行顺序上和原生Promise
有区别),把回调放入,等待确保异步执行
基础Promise
:
const isFunction = variable => typeof variable === 'function'
// 定义三个常量表示状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled',
const REJECTED = 'rejected'
class MyPromise {
constructor(func) {
this.onFulfilledArray = [] // 成功回调数组
this.onRejectedArray = [] // 失败回调数组
this.value = void 0
this.reason = void 0
this.status = PENDING
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(val) {
if (this.status !== PENDING) return
const self = this
const doResolve = () => {
if (self.status === PENDING) {
self.status = FULFILLED
self.value = val
// resolve里面将所有成功的回调拿出来执行
while (self.onFulfilledArray.length) {
const fn = self.onFulfilledArray.shift()
fn()
}
}
}
setTimeout(doResolve(), 0)
}
reject(err) {
if (this.status !== PENDING) return
const self = this
const doReject = () => {
if (self.status === PENDING) {
self.status = REJECTED
self.reason = err
// resolve里面将所有失败的回调拿出来执行
self.onRejectedArray.forEach(fn => {
fn()
})
}
}
setTimeout(doReject, 0)
}
then(onFulfilled, onRejected) {
let nextPromise = void 0
switch (this.status) {
case PENDING:
nextPromise = new MyPromise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let res = onFulfilled(self.value)
resolve(res)
} catch (error) {
reject(error)
}
})
this.onRejectedArray.push(() => {
try {
let res = onRejected(self.reason)
resolve(res)
} catch (e) {
reject(e)
}
})
})
break
case FULFILLED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = onFulfilled(self.value)
resolve(res)
} catch (e) {
reject(e)
}
})
break
case REJECTED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = onRejected(self.reason)
resolve(res)
} catch (e) {
reject(e)
}
})
onRejected(self.reason)
break
}
return nextPromise
}
catch(err) {
// 默认没有成功,只有失败
return this.then(void 0, err)
}
}
2.链式调用进阶版
以上的
Promise
存在一个问题,如果链式调用中Promise
返回的是普通值,应该把值包装成新的Promise
对象
- 每个
then
方法都返回一个新的Promise
对象(重点) - 如果
then
方法返回了一个Promise
对象,则需要查看它的状态- 如果状态是成功,则调用resolve方法,把成功的状态传递给它;
- 如果是失败的,则把失败的状态传递给下一个Promise对象
- 如果
then
方法中返回的是一个原始数据类型值(如 Number、String 等)就使用此值包装成一个新的Promise
对象返回 - 如果
then
方法中没有return
语句,则返回一个用undefined
包装的Promise
对象 - 如果 then 方法没有传入任何回调,则继续向下传递(值的传递特性)
- 如果是循环引用则需要抛出错误
修改then
方法,增加resolvePromise
函数
...
function resolvePromise(x, nextPromise, resolve, reject) {
// 处理三种情况:
// 1.循环引用
// 2.x 为MyPromise
// 3.x为基础类型
if (x === nextPromise) {
// x 和 nextPromise 指向同一对象,循环引用抛出错误。防止死循环
return reject(new TypeError('循环引用'))
} else if (x && (typeof x === 'object' || isFunction(x))) {
// x 是对象或者函数
let called = false // 避免多次调用
try {
let then = x.then // 判断对象是否有 then 方法
if (isFunction(then)) {
// then 是函数,就断定 x 是一个 MyPromise(根据Promise A+规范)
then.call(
x,
function(y) {
// 调用返回的MyPromise,用它的结果作为下一次then的结果
if (called) return
called = true
resolvePromise(y, nextPromise, resolve, reject) // 递归解析成功后的值,直到它是一个普通值为止
},
function(r) {
if (called) return
called = true
reject(r) // 取then时发生错误了
}
)
} else {
resolve(x) // 此时,x是一个普通对象
}
} catch (e) {
reject(e)
}
} else {
// x 是原始数据类型 / 没有返回值,这里即是undefined
resolve(x)
}
}
class MyPromise {
constructor(func) {
...
}
...
then(onFulfilled, onRejected) {
const self = this
// 如果onFulfilled不是函数,给一个默认函数,返回value
let realOnFulfilled = onFulfilled
if (!isFunction(realOnFulfilled)) realOnFulfilled = value => value
let realOnRejected = onRejected
if (!isFunction(onRejected)) {
realOnRejected = reason => {
if (reason instanceof Error) {
throw reason
} else {
throw new Error(reason)
}
}
}
let nextPromise = void 0
switch (this.status) {
case PENDING:
nextPromise = new MyPromise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let res = realOnFulfilled(self.value)
resolvePromise(res, nextPromise, resolve, reject)
// resolve(res)
} catch (error) {
reject(error)
}
// onFulfilled(self.value)
})
this.onRejectedArray.push(() => {
try {
let res = realOnRejected(self.reason)
resolve(res)
} catch (e) {
reject(e)
}
// onRejected(self.reason)
})
})
break
case FULFILLED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = realOnFulfilled(self.value)
resolvePromise(res, nextPromise, resolve, reject)
} catch (e) {
reject(e)
}
})
// onFulfilled(self.value)
break
case REJECTED:
nextPromise = new MyPromise((resolve, reject) => {
try {
let res = realOnRejected(self.reason)
resolvePromise(res, nextPromise, resolve, reject)
} catch (e) {
reject(e)
}
})
onRejected(self.reason)
break
}
return nextPromise
}
...
}
3.Promise.prototype.finally
实现
有两大重点:
- 无论当前这个 Promise 对象最终的状态是成功还是失败 ,finally 方法里的回调函数都会执行一次
- 在 finally 方法后面可以继续链式调用 then 方法,拿到当前这个 Promise 对象最终返回的结果
挂载在MyPromise.property
原型上,或者类似catch
实现:
MyPromise.prototype.finally = function(callback) {
return this.then(
data => {
return MyPromise.resolve(callback()).then(() => data)
},
err => {
return MyPromise.resolve(
callback().then(() => {
throw err
})
)
}
)
}
4.Promise.all()
和Promise.race()
实现
Promise.all()
:可将多个Promise
实例包装成一个新的Promise
实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject
失败状态的值
...
function isPromise(val) {
return val && typeof val.then === 'function'
}
class MyPromise() {
...
}
...
MyPromise.all = function(promises) {
if (!Array.isArray(promises)) {
throw new Error('Not a array')
}
return new MyPromise((resolve, reject) => {
let result = []
let times = 0 // 计数器
function processData(index, val) {
result[index] = val
if (++times === promises.length) {
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i]
if (isPromise(p)) {
// MyPromise对象
p.then(data => {
processData(i, data)
}, reject)
} else {
processData(i, p) // 普通值
}
}
})
}
Promise.race()
在执行多个异步操作中,不管结果是成功状态还是失败状态,只保留取第一个执行完成的异步操作的结果。其他的方法仍会执行,但结果会被抛弃
...
class MyPromise() {
...
}
...
MyPromise.race = function(promises) {
if (!Array.isArray(promises)) {
throw new Error('Not a array')
}
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return
} else {
for (let p of promises) {
p.then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
}
}
})
}
5.最终手写版源码
代码太长,直接参考源码仓库 examples | lianpf.github。欢迎star
思路很清晰的两篇文章:
最后, 希望大家早日实现:成为前端高手的伟大梦想!
欢迎交流~
本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK