js 手撕Promise2 实现静态方法 手撕async await generator
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js 手撕Promise2 实现静态方法 手撕async await generator相关的知识,希望对你有一定的参考价值。
实现静态方法
Promise.resolve
all finally
Promise.all方法接收一个p romise的iterable类型,并且只返回一个Promise实例。如果全都成功它们的值就作为then的第一个参数的入参,为数组[val1,val2,],但是如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。
实现:
static all(promiseArr)
return new Promise((resolve, reject) =>
let result = [];
let timers = 0;
const processSuccess = (index, val) =>
result[index] = val;
if (++timers === promiseArr.length)
resolve(result);
;
promiseArr.forEach((item, index) =>
if (item instanceof Promise)
item.then(
(data) =>
processSuccess(index, data);
,
//任何一个promise失败了,直接reject
reject
);
else
processSuccess(index, item);
);
);
思路就是直接for循环,先同步的走完所有,然后再判断是不是promise,再判断成功还是失败,只要一个失败,就reject。统计成功的个数以及对应的位置,即可实现。
finally的实现
无论成功失败,finally都会执行,而且resolve和reject的结果回贯穿过去往下面传。
但是finally有个特点,如果放在中间,当finally返回promise,程序会等待这个promise执行完毕再向下走。
当返回的promise是fuillied的时候,没有任何影响。
但是,当返回的promise是reject的时候,这时候会拦截前面的resolve,直接用reject去替代,就本来是resolve(3)的,直接变成reject(222)被捕获。所以返回的promise如果是reject就需要注意下。
实现:
思路:借助.then,因为then会返回新的promise。然后在then里面做处理。无论失败还是成功都调用finally传入的函数。
finally(cb)
return this.then(data=>
//这里返回一个新的promise,作为下一个then的结果。注意这里向下传递的是data,而不是cb的resolve值。
return Promise.resolve(cb()).then(()=>data)
,err=>
//注意这里返回一个Pormise,然后cb如果失败的话,不会走then,而是作为下一个catch的结果。
//但是cb成功的话是不会影响原本拒绝的效果,所以cb成功走了.then,在then中要抛出错误err,给下一个Catch,这样就不会影响整个流程。
return Promise.resolve(cb()).then(()=>throw err)
)
这里主要比较绕,Promise.reoslve是用来等待cb的执行的,如果cb中有异步操作,因为return 一个promise。,所以会等待这个promise的执行。然后原本结果是失败的话有两个处理方法,如果原本失败,但是cb返回的promise成功了,那么会走then。直接抛出原本的错误,不能影响。如果cb返回的promise失败了。就会替代原本的错误,不会走then,而是直接走到下一个catch。
race的实现
只要有一个成功或者失败就立马返回。
static race(promiseArr)
return new Promise((resolve, reject) =>
let isFirst = true;
promiseArr.forEach((item, index) =>
if (item instanceof Promise)
//谁先成功就调用谁,一旦状态改变再调用也无效。
item.then(resolve, reject);
else
resolve(item);
);
);
ract的思路就是直接遍历,谁先成功就调用resolve或者reject。状态一改变后续再调用就无效了。
实现promisify,promisefyAll
将原本回调函数的方法转为promise。
一般像readFile的方法都是通过回调函数获取值的,一般是最后一个参数,参数一般是err和data,err在第一位,err-first原则,基于这点就可以处理了。
function promisify(cb)
return function (...args)
return new Promise((resolve, reject) =>
cb(...args, (err, data) =>
if (err)
return reject(err);
resolve(data);
);
);
;
通过对最后一个参数的处理,如果失败就reject。如果成功就resolve。
实现promisifyAll,将一个对象中所有的通过回调函数的写法改成promise
function promisifyAll(obj)
let o = ;
for (let key in obj)
if (typeof obj[key] === "function")
o[`$keyPromise`] = promisify(obj[key]);
return o
遍历对象,借助promisefiy将其全部转化。
async await generator
- 一开始的异步通过回调函数解决,但是容易造成回调地狱。
- 而Promise只是优化了一下,并没有完全结果。
- gerarator,可以把函数的执行权交出去,执行,中断的形式。
- 终极解决方案: async await,基于generator的语法糖。是异步的最终解决办法。
generator是生成器,他执行之后返回一个迭代器。具体用法可以看mdn。
借助babel可以看到,核心就是靠一个while(1)的死循环加上switch case实现的。
我们可以自己实现一个这种函数。
这里实现regeneratorRuntime对象。
let regeneratorRuntime =
mark(genFn)
return genFn;
,
wrap(iteratorFn)
const context =
prev: 0,
sent: undefined,
now: undefined,
next: 0,
done: false, //表示迭代器是否执行
stop()
this.done = true;
,
;
let it = ;
it.next = function (val)
//上一次的next的参数作为上一次yield返回的值。
context.sent = context.now;
context.now = val;
return
value: iteratorFn(context),
done: context.done,
;
;
return it;
,
;
实现完毕。思路就是通过外部状态context控制开关,利用switch case匹配对应的值在去运行,每次调用Next就运行函数走swtich case流程,去赋值给外部状态context,然后再返回。然后状态变更,下次在调用next的时候就会继续往下走。
自动执行迭代器
可以通过while自动执行迭代器。
我们要拿到promise返回的data还需要通过下列一系列操作才能实现,虽说解决了回调地狱的问题,但是写法太过复杂。有一个库co,可以帮助我们直接获取生成器返回的值。
他接受一个迭代器,我们可以简单实现下co。
co的实现
思路:返回一个promise,然后递归遍历执行完接受的迭代器入参,再resolve出去即可。
function co(it)
return new Promise((resolve, reject) =>
//异步的迭代,只能使用next
function next(data)
let value, done = it.next(data);
if (done)
//迭代完毕,直接拿到最后的值resolve
reject(value);
else
//value不是promise也要变成Promise,如果失败了也要reject
Promise.resolve(value).then(next, reject);
//启动generator
next();
);
因为Promise属于异步,异步的迭代只能使用递归,所以这里需要判断如果生成器还没结束,就要继续调用next去走。如果失败则直接reject。
但是这样还需要引入co库。所以更简单的方法又诞生了,async+await
async await
简单的用法,async会将该函数包裹一层,使其返回一个promise,即使返回的是普通值rteturn 1,也会被包裹成类似于return Promise.resolve(1)这样。await表示在此处等待异步执行完毕再接着执行,即使后面跟着的不是promise,也会当作普通代码运行下去。
但其实async+await就是generator+co的语法糖。
可以看看babel的编译
有点多但可以看看关键实现:
可以看到第二个函数就是类似于co的实现,接受迭代器,然后递归调用next。跟成功就resolve,否则就继续调用next。
总结:
- generator生成器函数,本质就是通过全局状态context,还有一个死循环的switch case,每次Next一下,状态就往下变化。直到最后done变为true就一直是true了。而generator可以实现解决回调地狱,但是写法稍微复杂,co的出现让其写法更加简单,
- co的实现就是通过接受迭代器,返回一个promise,然后通过递归next一直调用it.next(val)直到done为true,再resolve出来。
- 最后es8出现的ascync+await就是异步的最终解决办法,其实现思路就跟generator+co一样的套路,只不过他是getnerator+co的语法糖。
以上是关于js 手撕Promise2 实现静态方法 手撕async await generator的主要内容,如果未能解决你的问题,请参考以下文章
js一些数组对象方法原理实现(手撕map,filter,every等)