1. 什么是Promise?
- Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
- Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending pending -> fulfilled pending -> rejected 一旦状态确定就不可更改
- resolve和reject函数是用来更改状态的 resolve: fulfilled reject: rejected
- then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
- then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
- 同一个promise对象下面的then方法是可以被调用多次的
- then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
2.下面来看几道题目,通过手写promise规范,可以从中得到答案
题一
题二
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {console.log('async2 end')}
async1()
setTimeout(function () {console.log('setTimeout')}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
}).then(function () {
console.log('promise1')
}).then(function () {
console.log('promise2')
})
console.log('script end')
// 结果如下
// script start
// async2 end
// Promise
// script end
// async1 end
// promise1
// promise2
// setTimeout
作者:小黎也
链接:https://juejin.cn/post/6844904180507738119
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
手写promise
一、准备工作
本次手写使用nodemon代替浏览器运行在node端。
- 处理化项目,yarn init -y
- 安装nodemon ,yarn add nodemon -D
- 新建index.js作为测试用例
- 创建myPromise.js 作为promise类
二、完成promise类,创建resolve和reject状态器,以及then回调方法(异步)
index.js
/*
1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
pending -> fulfilled
pending -> rejected
一旦状态确定就不可更改
3. resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected
4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
6. 同一个promise对象下面的then方法是可以被调用多次的
7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
*/
const MyPromise = require('./wangchunTest');
let promise=new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('基本使用')
},2222)
})
promise.then(value=>{
console.log(value)
},reason=>{
console.log(reason)
})
myPromise
// 静态存储
const FULFILLED="fulfilled"
const REJECTED="rejected"
const PENDING = 'pending'; // 等待
class MyPromise {
constructor (executor){
executor(this.resolve,this.reject)
}
// 初始为等待状态
status=PENDING
// 成功之后的值
value=undefined
// 失败之后的原因
reason=undefined
// 成功回调,改为数组,方便后续链式调用
successCallback=[]
// 失败回调,改为数组,方便后续链式调用
failCallback=[]
resolve=value=>{
if (this.status !== PENDING) return
this.status=FULFILLED
this.value=value
this.successCallback&&this.successCallback(value)
}
reject=reason=>{
if (this.status !== PENDING) return
this.status=REJECTED
this.reason=reason
this.failCallback&&this.failCallback(reason)
}
then=(successCallback,failCallback)=>{
if (this.status===FULFILLED){
successCallback(this.value)
}else if (this.status===REJECTED){
failCallback(this.reason)
}else{
// 等待状态下,存储两个回调,等待异步结束后调用
this.successCallback=successCallback
this.failCallback=failCallback
}
}
}
module.exports = MyPromise;
//
**- resolve和reject作为类实例化时传入的方法,执行哪个就更改对应的状态。
- 同时在then中执行对应的回调
- 当两个修改状态的方法中含有异步代码时候,then逻辑判断进入else,也就是等待状态
- 存储回调,等异步代码结束后,执行**
三、完成then的链式调用
// 静态存储
const FULFILLED="fulfilled"
const REJECTED="rejected"
const PENDING = 'pending'; // 等待
class MyPromise {
constructor (executor){
executor(this.resolve,this.reject)
}
// 初始为等待状态
status=PENDING
// 成功之后的值
value=undefined
// 失败之后的原因
reason=undefined
// 成功回调,改为数组,方便后续链式调用
successCallback=[]
// 失败回调,改为数组,方便后续链式调用
failCallback=[]
resolve=value=>{
// 修改为成功或者失败后不可进行变更
if (this.status !== PENDING) return
// 修改状态为成功
this.status=FULFILLED
// 保存成功之后的值
this.value=value
// 判断成功回调队列长度是否为0 ,如果出栈并调用
while (this.successCallback.length) this.successCallback.shift()(this.value)
}
reject=reason=>{
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return
// 将状态更改为失败
this.status=REJECTED
// 保存失败后的原因
this.reason=reason
// 判断失败回调队列长度是否为0 ,如果出栈并调用
while (this.failCallback.length) this.failCallback.shift()(this.reason)
}
then=(successCallback,failCallback)=>{
// 需要链式回调就需要返回promise对象,首先创建promise
let nextPromise=new MyPromise((resolve,reject)=>{
if (this.status===FULFILLED){
// 存储本次成功回调的值
let x=successCallback(this.value)
// 作为参数返回给下一个promise实例的resolve
resolve(x)
}else if (this.status===REJECTED){
failCallback(this.reason)
}else{
// 等待状态下,存储两个回调,等待异步结束后调用
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return nextPromise
}
}
module.exports = MyPromise;
链式调用的前提是,上一个then返回实例对象,故此处,在then中实例化promise,并且返回
四、添加错误捕获、自调用错误、then可选参数
index
let promise=new MyPromise((resolve,reject)=>{
// reject('失败')
setTimeout(()=>{
reject('das111d')
},2222)
})
promise.then().then().then(value=>console.log(value),reason=>console.log(reason))
myPromise
// 静态存储
const FULFILLED="fulfilled"
const REJECTED="rejected"
const PENDING = 'pending'; // 等待
class MyPromise {
constructor (executor){
try {
executor(this.resolve,this.reject)
}catch (e) {
// 捕获错误
this.reject(e)
}
}
// 初始为等待状态
status=PENDING
// 成功之后的值
value=undefined
// 失败之后的原因
reason=undefined
// 成功回调,改为数组,方便后续链式调用
successCallback=[]
// 失败回调,改为数组,方便后续链式调用
failCallback=[]
resolve=value=>{
// 修改为成功或者失败后不可进行变更
if (this.status !== PENDING) return
// 修改状态为成功
this.status=FULFILLED
// 保存成功之后的值
this.value=value
// 判断成功回调队列长度是否为0 ,如果出栈并调用
while (this.successCallback.length) this.successCallback.shift()()
}
reject = reason => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 判断失败回调是否存在 如果存在 调用
// this.failCallback && this.failCallback(this.reason);
while (this.failCallback.length) this.failCallback.shift()()
}
then(successCallback,failCallback){
// 参数可选
successCallback = successCallback ? successCallback : value => value;
// 参数可选
failCallback = failCallback ? failCallback : reason => {
throw reason
};
// 需要链式回调就需要返回promise对象,首先创建promise
let nextPromise=new MyPromise((resolve,reject)=>{
if (this.status===FULFILLED){
setTimeout(()=>{
try {
// 存储本次成功回调的值
let x=successCallback(this.value)
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
// 因为then方法是需要实例生成后才能调用的,所以此时拿不到实例队形,需要通过异步函数,放到栈底执行
resolvePromise(nextPromise,x,resolve,reject)
}catch (e) {
reject (e)
}
},0)
}else if (this.status===REJECTED){
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(nextPromise, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
}else{
// 等待状态下,存储两个回调,等待异步结束后调用
this.successCallback.push(()=>{
setTimeout(()=>{
try {
let x=successCallback(this.value)
resolvePromise(nextPromise,x,resolve,reject)
}catch (e) {
reject (e)
}
},0)
});
this.failCallback.push(()=>{
setTimeout(()=>{
try {
let x=failCallback(this.reason)
resolvePromise(nextPromise,x,resolve,reject)
}catch (e) {
reject (e)
}
},0)
})
}
})
return nextPromise
}
}
function resolvePromise(promsie2, x, resolve, reject) {
if (promsie2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
// promise 对象
// x.then(value => resolve(value), reason => reject(reason));
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise;
五、添加静态方法all、resolve,及finaaly、catch方法
index
/*
1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
pending -> fulfilled
pending -> rejected
一旦状态确定就不可更改
3. resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected
4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
6. 同一个promise对象下面的then方法是可以被调用多次的
7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
*/
const MyPromise = require('./wangchunTest');
function p1 () {
return new MyPromise(function (resolve, reject) {
setTimeout(function () {
reject('p1')
}, 2000)
})
}
function p2 () {
return new MyPromise(function (resolve, reject) {
resolve('p2')
// resolve('成功');
})
}
p2().finally(()=>{
console.log('finally');
return p1()
}).then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})
myPromise
// 静态存储
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
const PENDING = 'pending'; // 等待
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
// 捕获错误
this.reject(e)
}
}
// 初始为等待状态
status = PENDING
// 成功之后的值
value = undefined
// 失败之后的原因
reason = undefined
// 成功回调,改为数组,方便后续链式调用
successCallback = []
// 失败回调,改为数组,方便后续链式调用
failCallback = []
resolve = value => {
// 修改为成功或者失败后不可进行变更
if (this.status !== PENDING) return
// 修改状态为成功
this.status = FULFILLED
// 保存成功之后的值
this.value = value
// 判断成功回调队列长度是否为0 ,如果出栈并调用
while (this.successCallback.length) this.successCallback.shift()()
}
reject = reason => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 判断失败回调是否存在 如果存在 调用
// this.failCallback && this.failCallback(this.reason);
while (this.failCallback.length) this.failCallback.shift()()
}
then(successCallback, failCallback) {
// 参数可选
successCallback = successCallback ? successCallback : value => value;
// 参数可选
failCallback = failCallback ? failCallback : reason => {
throw reason
};
// 需要链式回调就需要返回promise对象,首先创建promise
let nextPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
// 存储本次成功回调的值
let x = successCallback(this.value)
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
// 因为then方法是需要实例生成后才能调用的,所以此时拿不到实例队形,需要通过异步函数,放到栈底执行
resolvePromise(nextPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(nextPromise, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
} else {
// 等待状态下,存储两个回调,等待异步结束后调用
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value)
resolvePromise(nextPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason)
resolvePromise(nextPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return nextPromise
}
finally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
return MyPromise.resolve(callback()).then(() => {
throw reason
})
})
}
catch (failCallback){
return this.then(undefined,failCallback)
}
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 无需被实例化即可调用,切多个实例化队形都可调用的需要作为静态方法
static all(array) {
// 结果数组
let result = []
// 标记成功添加计步
let index = 0
return new MyPromise((resolve, reject) => {
function addData(key, value) {
result[key] = value;
index++;
// 全部完成后返回,防止异步问题
if (index === array.length) {
resolve(result)
}
}
for (let i = 0; i < array.length; i++) {
let current = array[i]
if (current instanceof MyPromise) {
// 如果是promise对象
current.then(value => addData(i, value), reason => reject(reason))
} else {
// 如果仅仅是普通值
addData(i, array[i])
}
}
})
}
}
// 所有promise对象都需要使用的方法,应该用静态方法
function resolvePromise(promsie2, x, resolve, reject) {
if (promsie2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
// promise 对象
// x.then(value => resolve(value), reason => reject(reason));
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise;
本文结,通过上述五个步骤,可以了解到promise A+规范的手写流程,同时也可以深入了解到promise的执行时序。