摘要:简单实现前言你可能知道,的任务执行的模式有两种同步和异步。你已经实现了方法方法是一个很好用的方法。感兴趣的朋友可以自行去研究哈附上代码完整的实现个人博客链接
Promise 简单实现 前言
你可能知道,javascript 的任务执行的模式有两种:同步和异步。
异步模式非常重要,在浏览器端,耗时很长的操作(例如 ajax 请求)都应该异步执行,避免浏览器失去响应。
在异步模式编程中,我们经常使用回调函数。一不小心就可能写出以下这样的代码:
//事件1 doSomeThing1(function() { //事件2 doSomeThing2(function() { //事件3 doSomeThing3(); }); });
当你的需要异步执行的函数越来越多,你的层级也会越来越深。
这样的写法存在的缺点是:
不利于阅读
各个任务之间的高度耦合,难以维护
对异常的处理比较难
用 Promise 可以避免这种回调地狱,可以写成这样
//事件1 doSomeThing1() .then(function() { //事件2 return doSomeThing2(); }) .then(function() { //事件3 return doSomeThing3(); }) .catch(function() { //这里可以很方便的做异常处理 });
在市面上有许多库都实现了 Promise,例如:Q.js 、when.js ,es6 也将 Promise 纳入了标准中
es6 的 Promise 使用方法可以参考阮一峰的 http://es6.ruanyifeng.com/#do... ,我就不在做具体介绍
接下来,我们模仿 ES6 的 promise,一步一步来实现一个简单版的 Promise。
构造函数我们使用 Promise 的时候,
const promise = new Promise((resolve, reject)=>{ // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
Promise 是一个构造函数,接收一个函数,函数里有两个参数,resolve、reject。
我们可以这样子实现:
class PromiseA { constructor(executor) { const resolve = value => { this.resolve(value); }; const reject = err => { this.reject(err); }; try { executor(resolve, reject); } catch (err) { reject(err); } } resolve(value) { this.resultValue = value; } reject(error) { this.resultValue = error; } }then 方法
promise 中,用的最频繁的就是 then 方法,then 方法有两个参数,一个是 promise 成功时候的回调,一个是失败的回调。
实现方式为:
class PromiseA { constructor(executor) { const resolve = value => { this.resolve(value); }; const reject = err => { this.reject(err); }; try { executor(resolve, reject); } catch (err) { reject(err); } } resolve(value) { this.resultValue = value; if (this.fullfillCallback) { //++++++++ this.fullfillCallback(value); } } reject(error) { this.resultValue = error; if (this.rejectCallback) { //++++++++ this.rejectCallback(value); } } then(resolve, reject) { this.fullfillCallback = resolve; this.rejectCallback = resolve; } }then 方法有以下几个特性:
支持链式操作
每次 then 方法都是返回新的 Promise
当前 promise 的状态通过返回值传递给下一个 promise
错误冒泡,即如果当前 promise 没有提供 onReject 方法,会把错误冒泡到下一个 promise,方便处理
then(onResolve,onReject){ //返回新的Promise并支持链式操作 return new PromiseA((resolve,reject)=>{ this.fullfillCallback = (value)=>{ try { if (onResolve) { let newValue = onResolve(value); resolve(newValue); //将当前promise执行结果,传递给下一个promise } else { resolve(value); } } catch (err) { reject(err); } } //类似fullfillCallback this.rejectCallback = (value)=>{ try { if (onReject) { let newValue = onReject(value); resolve(newValue); } else { //错误冒泡 reject(value); } } catch (err) { reject(err); } } }); }
这样我们就实现了一个简单版的 then 方法了
加强版 then上面的实现,模拟了 then 方法的逻辑,但是还有一些缺点:
then 方法只能添加一个,例如
let p = new PromiseA(resolve => { setTimeout(() => { resolve(1); }, 0); }); p.then(value => { console.log("then-->" + value); }); //无输出,因为没触发到,被后一个覆盖了 p.then(value => { console.log("then2-->" + value); }); ////then---> 1
promise 没有状态,当 promsie 在添加 then 的时候已经完成了,没法得到结果
没有实现:如果上一个 promise 的返回值也是一个 Promise 对象时,则会等到这个 Promise resolve 的时候才执行下一个
为了解决第一点,引入了事件监听,简单的实现如下:export default class EventEmitter { constructor() { this._events = {}; } on(type, fn, context = this) { if (!this._events[type]) { this._events[type] = []; } this._events[type].push([fn, context]); } trigger(type) { let events = this._events[type]; if (!events) { return; } let len = events.length; let eventsCopy = [...events]; for (let i = 0; i < len; i++) { let event = eventsCopy[i]; let [fn, context] = event; if (fn) { fn.apply(context, [].slice.call(arguments, 1)); } } } }
所以进一步对 PromiseA 进行改造:
const STATUS = { PENDING: "pending", FULFILLED: "fulfilled", REJECTED: "rejected" }; const EventType = { fulfill: "fulfill", reject: "reject" }; class PromiseA { constructor(executor) { //初始化事件监听及状态 this.eventEmitter = new EventEmitter(); this.status = STATUS.PENDING; const resolve = value => { if (value instanceof PromiseA) { value.then( value => { this.resolve(value); }, error => { this.reject(error); } ); } else { this.resolve(value); } }; const reject = err => { this.reject(err); }; try { executor(resolve, reject); } catch (err) { reject(err); } } resolve(value) { //增加状态 if (this.status === STATUS.PENDING) { this.status = STATUS.FULFILLED; this.resultValue = value; this.eventEmitter.trigger(EventType.fulfill, value); } } reject(error) { //增加状态 if (this.status === STATUS.PENDING) { this.status = STATUS.REJECTED; this.resultValue = error; this.eventEmitter.trigger(EventType.reject, error); } } then(onResolve, onReject) { //根据状态不同处理 if (this.status === STATUS.PENDING) { return new PromiseA((resolve, reject) => { //增加事件监听 this.eventEmitter.on("fulfill", value => { try { if (onResolve) { let newValue = onResolve(value); resolve(newValue); } else { resolve(value); } } catch (err) { reject(err); } }); //增加事件监听 this.eventEmitter.on("reject", value => { try { if (onReject) { let newValue = onReject(value); resolve(newValue); } else { reject(value); } } catch (err) { reject(err); } }); }); } if ( this.status === STATUS.FULFILLED || this.status === STATUS.REJECTED ) { return new PromiseA((resolve, reject) => { let callback = returnValue; if (this.status === STATUS.FULFILLED) { callback = onResolve; } if (this.status === STATUS.REJECTED) { callback = onReject; } try { let newValue = callback(this.resultValue); resolveValue(newValue, resolve, reject); } catch (err) { reject(err); } }); } } }
到这里,我们的 then 方法基本就完成了。
最后还有一个小知识点,就是执行时机的问题:setTimeout(function() { console.log(4); }, 0); new Promise(function(resolve) { console.log(1); resolve(); }).then(function() { console.log(3); }); console.log(2); //输出结果会是: 1、2、3、4
promise.then,是异步的,属于 microtask,执行时机是本次事件循环结束之前,而 setTimeout 是 macrotask,执行时机是在下一次事件循环的开始之时
实现这个功能,我利用了第三方库 microtask 来模拟。所以 PromiseA 修改为:
resolve(value) { microtask(() => { if (this.status === STATUS.PENDING) { this.status = STATUS.FULFILLED; this.resultValue = value; this.eventEmitter.trigger(EventType.fulfill, value); } }) } reject(error) { microtask(() => { if (this.status === STATUS.PENDING) { this.status = STATUS.REJECTED; this.resultValue = error; this.eventEmitter.trigger(EventType.reject, error); } }); }
到此为止,我们的 then 方法已经大功告成了。最困难的一步已经解决了
catchPromise 跟普通回调的一大优势就是异常处理,我们推荐使用 Promise 的时候,总是使用 catch 来代替 then 的第二个参数。即是:
//bad let p = new Promise((resolve, reject) => { //...异步操作 }).then( value => { //成功 }, () => { //失败 } ); //good let p = new Promise((resolve, reject) => { //...异步操作 }) .then(value => { //成功 }) .catch(() => { //失败 });
接下来让我们实现 catch 方法:
catch(reject) { return this.then(null, reject); }
哈哈~ , 你没看错。你已经实现了 catch 方法
all 方法Promise.all 是一个很好用的方法。接受一个 promise 数组,然后等到所有的异步操作都完成了,就返回一个数组,包含对应的值
具体实现如下:
static all(promiseList = []) { //返回promise以便链式操作 return new PromiseA((resolve, reject) => { let results = []; let len = promiseList.length; let resolveCount = 0; //用于计数 let resolver = function (index, value) { resolveCount++; results[index] = value; if (resolveCount === len) { resolve(results); } }; //遍历执行所有的promise promiseList.forEach((p, i) => { if (p instanceof PromiseA) { p.then((value) => { resolver(i, value); }, (err) => { reject(err); }) } else { resolver(i, p); } }) }); }race 方法
race 方法为竞速,第一执行完的为准。所以只需循环一遍执行就可以了。
当有第一个将 Promise 的状态改变成 fullfilled 或 reject 之后,其他的就都无效了
static race(promiseList = []) { return new PromiseA((resolve, reject) => { promiseList.forEach((p, i) => { if (p instanceof PromiseA) { p.then((value) => { resolve(value); }, (err) => { reject(err); }) } else { resolve(p); } }) }) }小结
我们实现了一个简单版的 promise, 还有一些其他的方法在这里没有讲到。感兴趣的朋友可以自行去研究哈~
附上代码完整的实现 : https://github.com/chen434202...
个人博客链接:https://chen4342024.github.io...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/100233.html
摘要:近几年随着开发模式的逐渐成熟,规范顺势而生,其中就包括提出了规范,完全改变了异步编程的写法,让异步编程变得十分的易于理解。最后,是如此的优雅但也只是解决了回调的深层嵌套的问题,真正简化异步编程的还是,在端,建议考虑。 本篇,简单实现一个promise,主要普及promise的用法。 一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制...
摘要:实现的一个简单的如果有错误的地方,希望大家能够不吝赐教仅实现及方法最下方有完整代码开始一个对象接收的是一个这个接收两个参数当我们在内执行或的时候,就会调用内定义的和函数然后,和函数会改变的状态所以它应该是像下面这样的保存值记录状态为,为,为 实现的一个简单的ES6 Promise(如果有错误的地方,希望大家能够不吝赐教) 仅实现Promise及.then方法最下方有完整代码 开始 一个...
摘要:在和方法执行的时候订阅事件,将自己的回调函数绑定到事件上,属性是发布者,一旦它的值发生改变就发布事件,执行回调函数。实现和方法的回调函数都是,当满足条件对象状态改变时,这些回调会被放入队列。所以我需要在某个变为时,删除它们绑定的回调函数。 前言 按照文档说明简单地实现 ES6 Promise的各个方法并不难,但是Promise的一些特殊需求实现起来并不简单,我首先提出一些不好实现或者容...
摘要:为了降低异步编程的复杂性,所以。难理解请参考的误区以及实践异步编程的模式异步编程的种方法 异步编程 javascript异步编程, web2.0时代比较热门的编程方式,我们平时码的时候也或多或少用到,最典型的就是异步ajax,发送异步请求,绑定回调函数,请求响应之后调用指定的回调函数,没有阻塞其他代码的执行。还有像setTimeout方法同样也是异步执行回调的方法。 如果对异步编程...
摘要:近几年随着开发模式的逐渐成熟,规范顺势而生,其中就包括提出了规范,完全改变了异步编程的写法,让异步编程变得十分的易于理解。最后,是如此的优雅但也只是解决了回调的深层嵌套的问题,真正简化异步编程的还是,在端,建议考虑。 前段时间频频看到Promise这个词,今天发现腾讯AlloyTeam写得这篇很赞,遂转之。 原文链接 本篇,主要普及promise的用法。 一直以来,JavaScrip...
阅读 1813·2021-11-11 16:55
阅读 731·2019-08-30 15:53
阅读 3575·2019-08-30 15:45
阅读 617·2019-08-30 14:10
阅读 3241·2019-08-30 12:46
阅读 2107·2019-08-29 13:15
阅读 2006·2019-08-26 13:48
阅读 910·2019-08-26 12:23