上一篇咱們講了同步鏈式處理數據函子的概念。這一節,咱們來說異步。用到的概念很簡單,不須要有函數式編程的基礎。固然若是你看了那篇 《在你身邊你左右 --函數式編程別煩惱》 會更容易理解。這一篇咱們會完成一個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,更強大的功能,咱們慢慢加吧。