博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
十几行代码教你实现一个最简版的promise
阅读量:6116 次
发布时间:2019-06-21

本文共 3709 字,大约阅读时间需要 12 分钟。

最近研究了一下promise的实现,这篇文章使用了十几行代码实现了一个简单的promise;以便帮助读者对promise'有更深的了解。本篇文章实现的promise,遵循规范是 Promises/A+。

阅读本篇文章时,已经假定你会promise的基本使用和一些简单的es6语法;如果你还没掌握promise的基本使用,请学习完毕后再来。推荐可以看《promise迷你书》、《你不知道的js》及阮一峰老师的《ECMAScript 6 入门》。

promise的核心实现

首先,我们看一下一个promise的基本用法:

var p = new MyPromise((resolve) => {    setTimeout(() => {        resolve(20)    }, 300)})p.then( (msg) => console.log(msg) );

MyPromise是一个构造函数,这个构造函数会被传递一个函数;函数中有两个参数,是两个函数resolve,reject。另外一个promise有三种状态PENDING、RESOLVED、REJECTED。所以我们有以下的代码:

const PENDING = 0;const RESOLVED = 1;const REJECTED = 2;  function MyPromise(func){    let state = PENDING;    let value = null;    function resolve(newValue){        value = newValue;        state = RESOLVED;    }    function reject(err){        value = err;        state = REJECTED;    }    func(resolve, reject);}

然后我们实现then函数,每次then函数的执行会返回一个新的promise。

this.then = function(onFullFill, onReject){    return new MyPromise((resolve, reject) => {            })}

传递给then函数onFullFill函数返回值,会传递给第二个then中onFullFill中。即要能这样使用p.then( (msg) => msg ).then( data => console.log(data) );

这行代码实际是什么呢?让我们变换一下上面的代码:

p  .then( function fn1(msg){      return msg;  })  .then( function fn2(data){      console.log(data);  })//以上代码的实质fn2(fn1(msg))

我们要在then函数里面如何执行resolve函数呢?首先resolve函数是必须执行的,因为它要改变p.then()生成的promise的状态;其次,resolve这个函数还要能接受到onFullFill执行的值;以便传递给下一个回调函数。你可能想到了这种方案:

const PENDING = 0;const RESOLVED = 1;const REJECTED = 2;  function MyPromise(func){    let state = PENDING;    let value = null;    function resolve(newValue){        value = newValue;        state = RESOLVED;    }    function reject(err){        value = err;        state = REJECTED;    }    this.then = function(onFullFill, onReject){        return new MyPromise((resolve, reject) => {            resolve(onFullFill(value));        })    }    func(resolve, reject);}var p = new MyPromise((resolve) => {    setTimeout(() => {        resolve(20)    }, 300)})p.then( (msg) => msg ).then( data => console.log(data) );

但是当你把以上代码拼凑在一起,执行;打印出来的是null。why?

因为setTimeout(fn, 300)这行代码是异步执行的,而当promise中的state!==RESOLVED时,这行代码resolve(onFullFill(value));不应该执行。所以我们有了以下的优化:

function MyPromise(func){    let state = PENDING;    let value = null;    let handlers = [];    function resolve(newValue){        value = newValue;        state = RESOLVED;        handlers.forEach( handler => handle(handler));    }    function reject(err){        value = err;        state = REJECTED;    }    function handle(handler){        if(state === PENDING){            handlers.push(handler);            return;        }        handler.resolve(handler.onFullFill(value));    }    this.then = function(onFullFill, onReject){        return new MyPromise((resolve, reject) => {            handle({                resolve: resolve,                onFullFill: onFullFill            })        })    }    func(resolve, reject);}

这样在then函数里和resolve函数里我们都会执行handle函数,但只有在resolve函数执行后才会执行handler.resolve(handler.onFullFill(value))

现在还有一个问题,如果Promise中封装的不是异步操作,而是同步操作;那么resolve函数就会比then函数更先执行。

var p = new MyPromise((resolve, reject) => {    resolve('同步操作')})p.then(console.log)

所以我们执行resolve中的回调的时候,应该异步执行:

function resolve(newValue){    value = newValue;    state = RESOLVED;    setTimeout( () => {        handlers.forEach( handler => handle(handler));    }, 0)}

同时,由于then函数中可以接收一个promise;我们需要对这种情况进行处理:

function resolve = (newValue) => {  if(newValue && (typeof newValue === 'object' || typeonewValue === 'function') {    let then = newValue.then    if(typeof then === 'function'){      return then.call(newValue, resolve)    }    state = FULFILLED;  value = newValue;  setTimeout(() => {    handlers.forEach(handler => {      handle(handler)    })  }, 0)}

至此,我们已经完成了一个基本promise的实现。

转载地址:http://vevka.baihongyu.com/

你可能感兴趣的文章
B1010.一元多项式求导(25)
查看>>
10、程序员和编译器之间的关系
查看>>
前端学习之正则表达式
查看>>
配置 RAILS FOR JRUBY1.7.4
查看>>
AndroidStudio中导入SlidingMenu报错解决方案
查看>>
修改GRUB2背景图片
查看>>
Ajax异步
查看>>
好记性不如烂笔杆-android学习笔记<十六> switcher和gallery
查看>>
JAVA GC
查看>>
codeforce 599B Spongebob and Joke
查看>>
3springboot:springboot配置文件(外部配置加载顺序、自动配置原理,@Conditional)
查看>>
9、Dubbo-配置(4)
查看>>
前端第七天
查看>>
BZOJ 2190[SDOI2008]仪仗队
查看>>
图解SSH原理及两种登录方法
查看>>
[转载] 七龙珠第一部——第058话 魔境圣地
查看>>
【总结整理】JQuery基础学习---样式篇
查看>>
查询个人站点的文章、分类和标签查询
查看>>
基础知识:数字、字符串、列表 的类型及内置方法
查看>>
JSP的隐式对象
查看>>