Promise
Promise 是异步编程的一种解决方案。
Promise 有三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态),状态只能从 pending->fulfilled/rejected。状态一旦改变就不会再变化,状态一旦改变。
创造 Promise 实例后,它会立即执行。
Promise 解决的问题
Promise是用来解决两个问题:
- 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象;
Promise可以支持多个并发的请求,获取并发请求中的数据,这个Promise可以解决异步的问题,本身不能说Promise是异步的。
API
Promise 是一个构造函数,本身有 resolve、reject、all 方法,原型上有 then 和 catch 等方法。
构造函数
var p = new Promise((resolve, reject) => {})
- resolve :异步操作执行成功后的回调函数;
- reject:异步操作执行失败后的回调函数。
then 链式调用
p.then(resolveFn, rejectFn)
then方法可以接受两个参数,第一个对应resolve的回调函数,第二个对应reject的回调函数。
all
Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
all 接收一个数组参数,里面的值最终算返回 Promise 对象。
Promise.all([promise1, promise2, ...])
实现 Promise
基础版
- 可以创建promise对象实例;
- promise实例传入的异步方法执行成功就执行注册的成功回调函数,失败就执行注册的失败回调函数。
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = null
this.onRejectedFn = null
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
this.onFullFilledFn(this.value)
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectedFn(this.reason)
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
this.onFullFilledFn = onFullFilled
this.onRejectedFn = onRejected
}
支持同步任务
let promise = new MyPromise((resolve, reject) => {
resolve("同步任务执行")
})
promise.then((val) => {console.log(val)})
执行上面的代码,此时会报错:
"this.onFullFilledFn is not a function",这是因为我们的Promise实例是个同步任务,它的then方法还没被调用,所以回调函数还没有被注册,此时调用必然会报错。
解决方案:在reslove和reject里面用setTimeout进行包裹,使其到then方法执行之后再去执行
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = null
this.onRejectedFn = null
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn(this.value)
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = REJECTED
this.reason = reason
this.onRejectedFn(this.reason)
})
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
Promise 三种状态
MyPromise.prototype.then = function (onFullFilled, onRejected) {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
onFullFilled(this.value)
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onFullFilledFn = onFullFilled
this.onRejectedFn = onRejected
}
}
Promise.then 支持链式操作
我们目前的版本最多只能注册一个回调,我们来实现支持链式回调。
- 首先存储回调时要改为使用数组;
- 执行回调时,也要改成遍历回调数组执行回调函数;
- then 方法返回一个 Promise。
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = []
this.onRejectedFn = []
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn.forEach(fn => fn(this.value))
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = REJECTED
this.reason = reason
this.onRejectedFn.forEach(fn => fn(this.reason))
})
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
onFullFilled(this.value)
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onFullFilledFn.push(onFullFilled)
this.onRejectedFn.push(onRejected)
}
})
}
支持串行异步任务
目前then方法里只能传入同步任务,但是我们平常用promise.then方法里一般是异步任务,因为我们用promise主要用来解决一组流程化的异步操作,比如:
getUserId()
.then(getUserBalanceById)
.then(function (balance) {
// do sth
}, function (error) {
console.log(error);
});
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = []
this.onRejectedFn = []
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn.forEach(fn => fn(this.value))
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = REJECTED
this.reason = reason
this.onRejectedFn.forEach(fn => fn(this.reason))
})
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
if (this.status === PENDING) {
this.onFullFilledFn.push(() => {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
this.onRejectedFn.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
}
})
return promise
}
function resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
return reject(new Error('循环引用'))
}
// x 是除了 null 以外的对象或者函数
let called
if (x && (typeof x === 'function' || typeof x === 'object')) {
try {
const then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x)
}
}
实现 catch / all
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = []
this.onRejectedFn = []
// 在使用es6 的promise时,可以传入一个异步任务,也可以传入一个同步任务,利用setTimeout特性将具体执行放到then之后
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn.forEach(fn => fn())
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectedFn.forEach(fn => fn())
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
catch (onRejected) {
// catch 方法就是then方法没有成功的简写
return this.then(null, onRejected);
}
all (promises) {
//promises是一个promise的数组
return new MyPromise(function (resolve, reject) {
let arr = []; //arr是最终返回值的结果
let i = 0; // 表示成功了多少次
function processData(index, data) {
arr[index] = data;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
processData(i, data)
}, reject)
}
})
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
if (this.status === PENDING) {
this.onFullFilledFn.push(() => {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
this.onRejectedFn.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
}
})
return promise
}
function resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
return reject(new Error('循环引用'))
}
// x 是除了 null 以外的对象或者函数
let called
if (x && (typeof x === 'function' || typeof x === 'object')) {
try {
const then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x)
}
}
限制 Promise 并发的数量
class LimitPromise {
constructor (max) {
this._max = max // 异步任务“并发”上限
this._count = 0 // 当前正在执行的任务数量
this._taskQuequ = [] // 等待执行的任务队列
}
/**
* 调用器,将异步任务函数和它的参数传入
* @param caller 异步任务函数,它必须是async函数或者返回Promise的函数
* @param args 异步任务函数的参数列表
* @returns {Promise<unknown>} 返回一个新的Promise
*/
call (caller, ...args) {
return new Promise((resolve, reject) => {
const task = this._createTask(caller, args, resolve, reject)
if (this._count >= this._max) {
this._taskQuequ.push(task)
} else {
task()
}
})
}
/**
* 创建一个任务
* @param caller 实际执行的函数
* @param args 执行函数的参数
* @param resolve
* @param reject
* @returns {Function} 返回一个任务函数
* @private
*/
_createTask (caller, args, resolve, reject) {
return () => {
caller(args)
.then(resolve)
.catch(reject)
.finally(() => {
this._count --
if (this._taskQuequ.length) {
let task = this._taskQuequ.shift()
task()
this._count ++
}
})
}
}
}
test code:
const LimitPromise = require('limit-promise')
const request = require('./request')
// 请求上限
const MAX = 10
// 核心控制器
const limitP = new LimitPromise(MAX)
// 利用核心控制器包装request中的函数
function get (url, params) {
return limitP.call(request.get, url, params)
}
function post (url, params) {
return limitP.call(request.post, url, params)
}
// 导出
module.exports = {get, post}
const request = require('./request')
request.get('https://www.baidu.com')
.then((res) => {
// 处理返回结果
})
.catch(err => {
// 处理异常情况
})