手写promise
Posted betterangela
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写promise相关的知识,希望对你有一定的参考价值。
手写promise
高阶函数:应用最多的
发布订阅:观察者模式
promise实现原理
常见面试题
node 11.13 每个版本的事件环 & API 都有区别
高阶函数
函数套函数
如果函数的参数或返回值是一个函数,那我们就把这个函数叫做高阶函数
要写前端,最核心的就是函数
这里有AOP面向切片编程的思想
在原来逻辑之前切了一刀
作用域产生是根据函数定义的位置,执行时产生的是执行上下文
作用域是定义的时候就产生的
console.log('promise====================================');
function say(who)
console.log(who+' say hello');
Function.prototype.before=function (fn)
return (...args)=>
fn()
this(...args)
let newFn=say.before(function ()
console.log('say hello before');
)
newFn('Tom')
let oldPush=Array.prototype.push;
Array.prototype.push=function (...args)
console.log('push 触发...')
console.log(this)
oldPush.call(this,...args)
let arr=[1,3,5]
arr.push(7)
console.log(arr,arr)
面向切片编程AOP
我只关心核心逻辑,需要增强就增强一下就好了
这里涉及到闭包 执行上下文没有被销毁
all.js 本质是闭包原理
all-on-emit 发布订阅模式
发布订阅模式
核心思路:
订阅的时候把函数存到一个数组里面,然后发布的时候一个一个依次执行。
我先订阅好这件事情,订阅的时候事件是不会立即执行的。
发布订阅模式的特点:发布和订阅是没有关系的,两者是并行的关系
观察者模式
观察者存到被观察者中
写框架用这个用得比较多,写核心逻辑最多是用发布订阅
Promise*
基础用法
then链
异步代码中的报错是无法被外部try-catch捕获的
代码思路:
首先then需要返回一个新的promise
然后我的执行结果还要传递给下一个then,所以promise2要变为成功态,所以要拿到执行结果x然后resovle
resolvePromise
判断数据类型:
typeof:不能判断具体的引用数据类型和null 数组也是object
constructor: 判断构造函数
instanceof:在原型链上找
toString:万能
const PENDING='PENDING';
const RESOLVED='RESOLVED';
const REJECTED='REJECTED';
const resolvePromise=function (promise2,x,resolve,reject)
if(promise2===x)
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
if(typeof x === 'object' && x!=null || typeof x ==='function')
try
let then=x.then;
if(typeof then ==='function')
then.call(x,y=>
// resolve(y)
// 如果y又是一个promise,那么继续递归
resolvePromise(promise2,y,resolve,reject)
,r=>
reject(r)
)
else
resolve(x)
catch (e)
reject(e)
else
resolve(x)
class Promise
constructor(executor)
this.status=PENDING;
this.value=undefined;
this.reason=undefined;
this.onResovledCallbacks=[]
this.onRejectedCallbacks=[]
let resolve=(value) =>
if(this.status===PENDING)
this.status=RESOLVED;
this.value=value;
// 成功
this.onResovledCallbacks.forEach(fn=>fn())
let reject=(reason) =>
if(this.status===PENDING)
this.status=RESOLVED;
this.reason=reason;
// 失败
this.onResovledCallbacks.forEach(fn=>fn())
try
executor(resolve,reject)
catch (e)
reject(e)
then(onresolved,onrejected)
onresolved=typeof onresolved==='function'?onresolved:(data)=>data;
onrejected=typeof onrejected==='function'?onrejected:(err)=>
throw err;
// 每一个then都返回一个新的promise
let promise2=new Promise((resolve,reject)=>
if(this.status===RESOLVED)
setTimeout(()=>
try
let x=onresolved(this.value)
resolvePromise(promise2,x,resolve,reject)
catch (e)
reject(e)
,0)
if(this.status===REJECTED)
setTimeout(()=>
try
let x=onrejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
catch (e)
reject(e)
,0)
if(this.status===PENDING)
this.onResovledCallbacks.push(()=>
setTimeout(()=>
try
let x=onresolved(this.value)
resolvePromise(promise2,x,resolve,reject)
catch (e)
reject(e)
,0)
)
this.onRejectedCallbacks.push(()=>
setTimeout(()=>
try
let x=onrejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
catch (e)
reject(e)
,0)
)
)
return promise2;
Promise.defer=Promise.deferred=function()
let dfd=
dfd.promise=new Promise((resolve,reject)=>
dfd.resolve=resolve;
dfd.reject=reject;
)
return dfd;
function isPromise(obj)
if(typeof obj ==='object' && obj !=null || typeof obj==='function')
if(obj.then && typeof obj.then ==='function') return true;
return false;
Promise.all=function(values)
return new Promise((resolve,reject)=>
let results=[],times=0;
function processData(index,data)
times++;
results[index]=data;
if(times===values.length)
resolve(results)
for(let i=0;i<values.length;i++)
let current=values[i];
if(isPromise(current))
current.then(res=>processData(i,res))
else
processData(i,values[i])
)
module.exports=Promise;
finally
finally会承接上一个promise的状态,透过finally,传递给下一个then/catch
finally就是then的别名,就是通过then去实现的。如果上一个是成功就把成功往下传,上一个是失败就把失败往下传
Generator
迭代器,就是一个对象,具有next方法,并且next方法返回value:xxx,done:xxx
生成器就是产出迭代器,就是生成迭代器
let obj=
0:'a',
1:'b',
length:2,
*[Symbol.iterator]()
for(let i=0;i<this.length;i++)
yield this[i]
// let i=0;
// return
// next:()=> // 直接使用箭头函数
// return
// value:this[i],
// done:i++===this.length
//
//
//
let arr=[...obj];
console.log(arr);
generator+co
也就是async/await的原理
const fs=require('fs').promises;
function *read()
try
const content=yield fs.readFile('./name11.txt','utf8');
const age=yield fs.readFile('./age.txt','utf8');
return age;
catch (e)
console.log('err=======',e)
let it=read();
// const value,done=it.next();
// Promise.resolve(value).then(data=>
// console.log('data=======');
// console.log(data)
// const value,done=it.next(data);
// Promise.resolve(value).then(data=>
// console.log('data=======');
// console.log(data)
// const value,done=it.next(data)
// )
// )
// 核心代码
function co(it)
return new Promise((resolve,reject)=>
function deepNext(it,data)
const value,done=it.next(data);
if(!done)
Promise.resolve(value).then(data=>
deepNext(it,data) // 把yield结果传递给下一个it.next
,(err)=>it.throw(err))
else
resolve(data)
deepNext(it)
)
co(it)
小扩展:
其实还是AOP思想
总结
手写Promise
手写Promise
在本文中,我们通过手写实现一个符合Promise/A+规范的Promise来深入了解Promise, 而且手写Promise也是一道大厂面试常考题。在进入正题之前,推荐各位阅读一下Promise/A+规范 (网上有很多解释,可以自行搜索),这样才能更深入的理解本文中的代码。
实现一个简易版的Promise
先来搭建函数的大体框架
// 创建3个常量表示Promise的3个状态 const PENDING = ‘pending‘ const RESOLVED = ‘resolved‘ const REJECTED = ‘rejected‘ function MyPromise(fn){ // 创建常量that保存this, 因为Promise代码可能会异步执行,用于获取正确的this对象 const that = this // 一开始Promise的状态应该是pending this.state = PENDING // value变量用于保存resolve或者reject中传入的值 that.value = null // 用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用 that.resolvedCallbacks = [] that.rejectedCallbacks = [] // 待完善 resolve 和 reject 函数 // 待完善执行 fn 函数 }
接下来完善resolve和reject函数,添加在MyPromise内部
// 待完善的 resolve 和 reject 函数 function resolve(value) { console.log(‘执行resolve‘) // 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态 if (that.state === PENDING) { // // 将当前状态更改为对应状态,并且将传入的值赋值给value that.state = RESOLVED that.value = value // 状态改变时,指定回调 that.resolvedCallbacks.map(cb => cb(that.value)) } } function reject(value) { // 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态 if (that.state === PENDING) { // 将当前状态更改为对应状态,并且将传入的值赋值给value that.state = REJECTED that.value = value // 状态改变时执行回调 that.rejectedCallbacks.map(cb => cb(that.value)) } }
完成以上两个函数后,就该实现如何执行Promise中传入的函数了
// 待完善执行 fn 函数 try { // 执行传入的参数并且将之前两个函数当做参数传进去 fn(resolve, reject) }catch (e) { // 可能执行函数过程中会遇到错误,需要捕获错误并且执行 reject 函数 reject(e) }
最后实现较为复杂的then函数
MyPromise.prototype.then = function (onFulfilled, onRejected) { console.log(‘执行then‘) const that = this // 判断两个参数是否为函数类型,因为这两个参数是可选参数,如果不是函数,则直接返回相应的value或reason onFulfilled = typeof onFulfilled === ‘function‘ ? onFulfilled : v => v onRejected = typeof onRejected === ‘function‘ ? onRejected : r => { throw r } // 当函数是等待态时,就往回调函数数组中push函数 if (that.state === PENDING) { console.log(‘Pending‘) that.resolvedCallbacks.push(onFulfilled) that.rejectedCallbacks.push(onRejected) } // 如果不是等待态,就执行相对应的函数 if (that.state === RESOLVED) { onFulfilled(that.value) } if (that.state === REJECTED) { onRejected(that.value) } }
最后,使用MyPromise
new MyPromise((resolve, reject) => { // 这里是异步,所以回调会先存入resolvedCallbacks中 setTimeout(() => { resolve(1) }, 0) }).then(value => { console.log(value) })
以上就是简单版的Promise实现,接下来实现更为完整版的Promise的解析。
以上是关于手写promise的主要内容,如果未能解决你的问题,请参考以下文章
前端技能树,面试复习第 52 天—— 手写代码:Javascript 基础考核