最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

如何从零开始利用js手写一个Promise库详解

来源:懂视网 责编:小采 时间:2020-11-27 22:16:28
文档

如何从零开始利用js手写一个Promise库详解

如何从零开始利用js手写一个Promise库详解:前言 ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的实现。ES6 的目标,是使得 JavaScript 语言可以用来编写大型的复杂的应用程序,成为企业级开发语言。 概念 ES6 原生提供了 Promise 对象。 所谓 Promise,就
推荐度:
导读如何从零开始利用js手写一个Promise库详解:前言 ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的实现。ES6 的目标,是使得 JavaScript 语言可以用来编写大型的复杂的应用程序,成为企业级开发语言。 概念 ES6 原生提供了 Promise 对象。 所谓 Promise,就

前言

ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的实现。ES6 的目标,是使得 JavaScript 语言可以用来编写大型的复杂的应用程序,成为企业级开发语言。

概念

ES6 原生提供了 Promise 对象。

所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。

三道思考题

刚开始写前端的时候,处理异步请求经常用callback,简单又顺手。后来写着写着就抛弃了callback,开始用promise来处理异步问题。promise写起来确实更加优美,但由于缺乏对它内部结构的深刻认识,每次在遇到一些复杂的情况时,promise用起来总是不那么得心应手,debug也得搞半天。

所以,这篇文章我会带大家从零开始,手写一个基本能用的promise。跟着我写下来以后,你会对promise是什么以及它的内部结构有一个清楚的认知,未来在复杂场景下使用promise也能如鱼得水。

而且,为了检验大家是否真的完全掌握了promise,我会在文章结尾出几道跟promise相关的练习题。说是练习题,其实都是大家项目中会遇到的真实场景的抽象,熟练掌握可以帮助大家在前端方面更上一层楼。

提前将三道练习题给出来,大家可以先不看下文的内容,在脑海里大概构思下你会怎么解决:

  • promise array的链式调用?
  • promise怎么做并发控制?
  • promise怎么做异步缓存?
  • 以上三道思考题其实跟你用不用promise并没有多大关系,但是如果你不深刻理解promise想要解决这三个问题还真不是那么轻松的。

    什么是Promise

    回到正文,什么是Promise?说白了,promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

    首先,ES6规定Promise对象是一个构造函数,用来生成Promise实例。然后,这个构造函数接受一个函数(executor)作为参数,该函数的两个参数分别是resolve和reject。最后,Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数(onFulfilled和onRejected)。

    具体的使用方法,用代码表现是这样:

    const promise = new Promise(function(resolve, reject) {
     // ... some code
    
     if (/* 异步操作成功 */){
     resolve(value);
     } else {
     reject(error);
     }
    });
    
    promise.then(function(value) {
     // success
    }, function(error) {
     // failure
    });

    理解了这个后,我们就可以大胆的开始构造我们自己的promise了,我们给它取个名字:CutePromise

    实现一个Promise:CutePromise

    我们直接用ES6的class来创建我们的CutePromise,对ES6语法还不熟悉的,可以先读一下我的另外两篇介绍ES6核心语法的文章后再回来。30分钟掌握ES6/ES2015核心内容(上) 、30分钟掌握ES6/ES2015核心内容(下)

    class CutePromise {
     // executor是我们实例化CutePromise时传入的参数函数,它接受两个参数,分别是resolve和reject。
     // resolve和reject我们将会定义在constructor当中,供executor在执行的时候调用
     constructor(executor) {
     const resolve = () => {}
     const reject = () => {}
     executor(resolve, reject)
     }
    
     // 为实例提供一个then的方法,接收两个参数函数,
     // 第一个参数函数必传,它会在promise已成功(fulfilled)以后被调用
     // 第二个参数非必传,它会在promise已失败(rejected)以后被调用
     then(onFulfilled, onRejected) {}
    }

    创建了我们的CutePromise后,我们再来搞清楚一个关键点:Promise 对象的状态。

    Promise 对象通过自身的状态,来控制异步操作。一个Promise 实例具有三种状态:

  • 异步操作未完成(pending)
  • 异步操作成功(fulfilled)
  • 异步操作失败(rejected)
  • 上面三种状态里面,fulfilled和rejected合在一起称为resolved(已定型)。状态的切换只有两条路径:第一种是从pending=>fulfilled,另一种是从pending=>rejected,状态一旦切换就不能再改变。

    现在我们来为CutePromise添加状态,大概流程就是:

    首先,实例化初始过程中,我们先将状态设为PENDING,然后当executor执行resolve的时候,将状态更改为FULFILLED,当executor执行reject的时候将状态更改为REJECTED。同时更新实例的value。

    constructor(executor) {
     ...
     this.state = 'PENDING';
     ...
     const resolve = (result) => {
     this.state = 'FULFILLED';
     this.value = result;
     }
     const reject = (error) => {
     this.state = 'REJECTED';
     this.value = error;
     }
     ...
    }

    再来看下我们的then函数。then函数的两个参数,onFulfilled表示当promise异步操作成功时调用的函数,onRejected表示当promise异步操作失败时调用的函数。假如我们调用then的时候,promise已经执行完成了(当任务是个同步任务时),我们可以直接根据实例的状态来执行相应的函数。假如promise的状态还是PENDING, 那我们就将onFulfilled和onRejected直接存储到chained这个变量当中,等promise执行完再调用。

    constructor(executor) {
     ...
     this.state = 'PENDING';
     
     // chained用来储存promise执行完成以后,需要被依次调用的一系列函数
     this.chained = [];
     const resolve = (result) => {
     this.state = 'FULFILLED';
     this.value = result;
     
     // promise已经执行成功了,可以依次调用.then()函数里的onFulfilled函数了
     for (const { onFulfilled } of this.chained) {
     onFulfilled(res);
     }
     }
    
     ...
    }
    then(onFulfilled, onRejected) {
     if (this.state === 'FULFILLED') {
     onFulfilled(this.value);
     } else if (this.state === 'REJECTED') {
     onRejected(this.value);
     } else {
     this.$chained.push({ onFulfilled, onRejected });
     }
    }

    这样我们就完成了一个CutePromise的创建,下面是完整代码,大家可以到控制台测试一下:

    class CutePromise {
     constructor(executor) {
     if (typeof executor !== 'function') {
     throw new Error('Executor must be a function');
     }
    
     this.state = 'PENDING';
     this.chained = [];
     const resolve = res => {
     if (this.state !== 'PENDING') {
     return;
     }
    
     this.state = 'FULFILLED';
     this.internalValue = res;
     for (const { onFulfilled } of this.chained) {
     onFulfilled(res);
     }
     };
     const reject = err => {
     if (this.state !== 'PENDING') {
     return;
     }
     this.state = 'REJECTED';
     this.internalValue = err;
     for (const { onRejected } of this.chained) {
     onRejected(err);
     }
     };
    
     try {
     executor(resolve, reject);
     } catch (err) {
     reject(err);
     }
     }
     
     then(onFulfilled, onRejected) {
     if (this.state === 'FULFILLED') {
     onFulfilled(this.internalValue);
     } else if (this.$state === 'REJECTED') {
     onRejected(this.internalValue);
     } else {
     this.chained.push({ onFulfilled, onRejected });
     }
     }
    }

    提供一下测试代码:

    let p = new CutePromise(resolve => {
     setTimeout(() => resolve('Hello'), 100);
    });
    p.then(res => console.log(res));
    p = new CutePromise((resolve, reject) => {
     setTimeout(() => reject(new Error('woops')), 100);
    });
    p.then(() => {}, err => console.log('Async error:', err.stack));
    p = new CutePromise(() => { throw new Error('woops'); });
    p.then(() => {}, err => console.log('Sync error:', err.stack));

    总结

    声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文档

    如何从零开始利用js手写一个Promise库详解

    如何从零开始利用js手写一个Promise库详解:前言 ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的实现。ES6 的目标,是使得 JavaScript 语言可以用来编写大型的复杂的应用程序,成为企业级开发语言。 概念 ES6 原生提供了 Promise 对象。 所谓 Promise,就
    推荐度:
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top