上一篇咱们讲了同步链式处理数据函子的概念。这一节,咱们来说异步。用到的概念很简单,不须要有函数式编程的基础。固然若是你看了那篇 《在你身边你左右 --函数式编程别烦恼》 会更容易理解。这一篇咱们会完成一个Promise代码的编写。本文会从实现一个只有十几行代码可以解决异步链式调用问题的简单的Promise开始。而后逐渐完善增长功能。node
本文代码在个人github git
咱们先来回顾一下同步链式调用。github
class Functor{
constructor (value) {
this.value = value ;
}
map (fn) {
return Functor.of(fn(this.value))
}
}
Functor.of = function (val) {
return new Functor(val);
}
Functor.of(100).map(add1).map(add1).map(minus10)
// var a = Functor.of(100);
// var b = a.map(add1);
// var c = b.map(add1);
// var d = c.map(minus10);
复制代码
那么若是当a的值是异步产生的,咱们该何如传入this.value值呢?编程
function executor(resolve){
setTimeout(()=>{resolve(100)},500)
}
复制代码
咱们模拟一下经过setTimeout500毫秒后拿到数据100。其实也很简单,咱们能够传进去一个resolve回调函数去处理这个数据。数组
class Functor {
constructor (executor) {
let _this = this;
this.value = undefined;
function resolve(value){
_this.value = value;
}
executor(resolve)
}
}
var a = new Functor(executor);
复制代码
这样咱们就轻松的完成了a这个对象的赋值。那么咱们怎么用方法去处理这个数据呢?promise
class Functor {
constructor (executor) {
let _this = this;
this.value = undefined;
this.callback = null;
function resolve(value){
_this.value = value;
_this.callback()
}
executor(resolve)
}
map (fn) {
let self = this;
return new Functor((resolve) => {
self.callback = function(){
let data = fn(self.value)
resolve(data)
}
})
}
}
new Functor(executor).map(add1).map(add1)
复制代码
如今咱们已经实现了异步的链式调用,咱们来具体分析一下,都发生了什么。bash
注意:这时map中this指向的是a函子,可是 new Functor((resolve) => {}中resolve是B的异步
咱们再来分析一下异步结束以后,回调函数中的resolve是如何执行的。async
本节代码:promise1.js函数式编程
嗯,这就是promise做为函子实现的处理异步操做的基本原理。它已经可以解决了简单的异步调用问题。虽然代码很少,但这是promise处理异步调用的核心。接下来咱们会不断继续实现其余功能。
若是咱们像下面同时调用a这个函子。你会发现,它实际上只执行了c。
var a = new Functor(executor);
var b = a.map(add);
var c = a.map(minus);
复制代码
缘由很简单,由于上面咱们学过,b先给a的callback赋值,而后c又给a的callback赋值。因此把b给覆盖掉了就不会执行啦。解决这个问题很简单,咱们只须要让callback变成一个数组就解决啦。
class MyPromise {
constructor (executor) {
let _this = this;
this.value = undefined;
this.callbacks = [];
function resolve(value){
_this.value = value;
_this.callbacks.forEach(item => item())
}
executor(resolve)
}
then (fn) {
return new MyPromise((resolve) => {
this.callbacks.push (()=>{
let data = fn(this.value)
console.log(data)
resolve(data)
})
})
}
}
var a = new MyPromise(executor);
var b = a.then(add).then(minus);
var c = a.then(minus);
复制代码
咱们都知道,在异步调用的时候,咱们每每不能拿到数据,返回一个错误的信息。这一小节,咱们对错误进行处理。
function executor(resolve,reject){
fs.readFile('./data.txt',(err, data)=>{
if(err){
console.log(err)
reject(err)
}else {
resolve(data)
}
})
}
复制代码
如今咱们定义出这个reject
class MyPromise {
constructor (executor) {
let _this = this;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
function resolve(value){
_this.value = value;
_this.onResolvedCallbacks.forEach(item => item())
}
function reject(reason){
_this.reason = reason;
_this.onRejectedCallbacks.forEach(item => item());
}
executor(resolve, reject);
}
then (fn,fn2) {
return new MyPromise((resolve,reject) => {
this.onResolvedCallbacks.push (()=>{
let data = fn(this.value)
console.log(data)
resolve(data)
})
this.onRejectedCallbacks.push (()=>{
let reason = fn2(this.reason)
console.log(reason)
reject(reason)
})
})
}
}
复制代码
本节代码:promise3.js
这时候将executor函数封装到asyncReadFile异步读取文件的函数
function asyncReadFile(url){
return new MyPromise((resolve,reject) => {
fs.readFile(url, (err, data) => {
if(err){
console.log(err)
reject(err)
}else {
resolve(data)
}
})
})
}
var a = asyncReadFile('./data.txt');
a.then(add,mismanage).then(minus,mismanage);
复制代码
这就是咱们平时封装异步Promise函数的过程。但这是过程有没有以为在哪见过。若是以前executor中的'./data.txt'咱们是经过参数传进去的那么这个过程不就是上一节咱们提到的柯里化。
本节代码:promise4.js
咱们再来总结一下上面的过程。
那么咱们如何解决reslove以后a函子的then调用问题呢,其实reslove以后,咱们已经有了value值,那不就是咱们最开始讲的普通函子的链式调用吗?因此如今咱们只须要标记出,函子此时的状态,再决定如何调用then就好啦
class MyPromise {
constructor (executor) {
let _this = this;
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
function resolve(value){
if (_this.status === 'pending') {
_this.status = 'fulfilled';
_this.value = value;
_this.onResolvedCallbacks.forEach(item => item())
}
}
function reject(reason){
if (_this.status === 'pending') {
_this.status = 'rejected';
_this.reason = reason;
_this.onRejectedCallbacks.forEach(item => item());
}
}
executor(resolve, reject);
}
then (fn,fn2) {
return new MyPromise((resolve,reject) => {
if(this.status === 'pending'){
this.onResolvedCallbacks.push (()=>{
let data = fn(this.value)
console.log(data)
resolve(data)
})
this.onRejectedCallbacks.push (()=>{
let reason = fn2(this.reason)
console.log(reason)
reject(reason)
})
}
if(this.status === 'fulfilled'){
let x = fn(this.value)
resolve(x)
}
if(this.status === 'rejected'){
let x = fn2(this.value)
reject(x)
}
})
}
}
var a = asyncReadFile('./data.txt');
a.then(add,mismanage).then(add,mismanage).then(add,mismanage);
复制代码
咱们分析一下上面这个过程
其实就多了一个参数,而后判断了一下,很简单。那么咱们如今来分析一下,当咱们调用fulfilled状态下的a的执行过程
setTimeout(()=>{ d = a.then(add);} ,2000)
value:"1"
复制代码
咱们来想一个问题,若是(2)中fn是一个异步操做,d后边继续调用then方法,此刻pending状态就不会改变,直到resolve执行。那么then的方法就会加到callback上。就又回到咱们以前处理异步的状态啦。因此这就是为何Promise可以解决回调地狱
参考代码:promise5.js
好了,咱们如今来看传进去的方法fn(this.value) ,咱们须要用上篇讲的Maybe函子去过滤一下。
then (onResolved,onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}
return new MyPromise((resolve,reject) => {
if(this.status === 'pending'){
this.onResolvedCallbacks.push (()=>{
let x = onResolved(this.value)
resolve(x)
})
this.onRejectedCallbacks.push (()=>{
let x = onRejected(this.reason)
reject(x)
})
}
if(this.status === 'fulfilled'){
let x = onResolved(this.value)
resolve(x)
}
if(this.status === 'rejected'){
let x = onRejected(this.value)
reject(x)
}
})
}
复制代码
参考代码:promise6.js
这一篇先写到这里吧。最后总结一下,Promise的功能很强大,就是少年派的奇幻漂流同样。虽然旅程绚烂多彩,但始终陪伴你的只有那只老虎。Promise也是同样,只要掌握其核心函子的概念,其余问题就比较好理解啦。这里只实现了一个简单的Promise,更强大的功能,咱们慢慢加吧。