最近研究了一下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))
。
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的实现。