小邵教你玩转promise源码

前言:你们好,我叫邵威儒,你们都喜欢喊我小邵,学的金融专业却凭借兴趣爱好入了程序猿的坑,从大学买的第一本vb和自学vb,我就与编程结下不解之缘,随后自学易语言写游戏辅助、交易软件,至今进入了前端领域,看到很多朋友都写文章分享,本身也弄一个玩玩,如下文章纯属我的理解,便于记录学习,确定有理解错误或理解不到位的地方,意在站在前辈的肩膀,分享我的对技术的通俗理解,共同成长!javascript

后续我会陆陆续续更新javascript方面,尽可能把javascript这个学习路径体系都写一下
包括前端所经常使用的es六、angular、react、vue、nodejs、koa、express、公众号等等
都会从浅到深,从入门开始逐步写,但愿能让你们有所收获,也但愿你们关注我~前端

源码地址:github.com/iamswr/prom…
文章列表:juejin.im/user/5a84f8…vue

Author: 邵威儒
Email: 166661688@qq.com
Wechat: 166661688
github: github.com/iamswr/java


JavaScript做为单线程语言,其特色也是其缺陷,特色就是不用处理多线程引起的占用资源、冲突啪啦啪啦等,缺陷就是同一时间,只能作一件事情,那么会存在一个问题,网络传输是有延迟的,好比A发一条信息到B服务器,在B服务器还没返回信息给A时,那么A就会一直在等待接收信息,会形成页面的假死,那么该怎么办?俗话说得好,程序猿改变世界,因而乎出现了异步的概念,我会分如下几点,去讲述我对前端异步的理解:node

  1. callback
  2. 闭包、高阶函数
  3. promise
  4. generator ( 文章地址:juejin.im/post/5b7512…
  5. async / await ( 文章地址:juejin.im/post/5b7512…

一.callback

回调函数被认为是一种高级函数,一种被做为参数传递给另外一个函数(在这称做"otherFunction")的高级函数,回调函数会在otherFunction内被调用(或执行)。回调函数的本质是一种模式(一种解决常见问题的模式),所以回调函数也被称为回调模式。react

是否是看起来一头懵逼,到底什么是回调函数?有什么做用?我对回调函数理解,从真正意义上让我忽然间恍然大悟的,就是当初研究jQuery底层源码的时候,咱们看如下一段代码:jquery

<img src='../a.jpg'></img>
<img src='../b.jpg'></img>
<img src='../c.jpg'></img>

$("img").attr("title",function(index,attr){
    console.log(index) // 依次返回0 1 2
});
复制代码

在attr方法的第二个参数,传入了一个function,而该函数,会依次获取$('img')的DOM对象对应index和attr,咱们能够在该function里,写咱们须要的业务逻辑,那么这样有什么好处呢?git

个人理解是,假如我要封装一个库,造一个轮子,那么要考虑到通用性和复用性而且提供一个途径,让使用者任意发挥想象写业务逻辑,而且把相关可能使用到的参数,都传给使用者。es6

假设,咱们如今有一个需求,要写一个判断类型的方法,常见的方法有如下几种:github

- typeOf // 简单的数据类型判断,栈区
 - instanceof // 复杂的数据类型,堆区
 - constructor // 复杂的数据类型,主要是用在继承的改写指向的构造函数,不多用于判断类型
 - Object.prototype.toString.call() // 绝大多数库底层都是使用该方式,返回值如[object String]
复制代码

首先,咱们写一个isType方法

function isType(content,type,fn){
    // 类型判断
    let t = Object.prototype.toString
                              .call(content)
                              .replace(/\[object\s|\]/g,'')
                              
    // 判断完成后,执行传入的callback函数
    fn(type,t)
}
复制代码

如今咱们要判断一个值的类型,而后拿到这个类型,最终执行咱们须要作的事情

isType('hello swr','String',function(type,t){ // 做为参数传入的函数,接收isType函数内的fn中type和t这两个参数
    console.log(type === t) // true
})
复制代码

首先执行了isType函数,而后执行isType函数内部的代码,isType内的fn(type,t),这里的fn方法,实际就是咱们传入的第三个参数,即里面只有一行console.log(type === t)的函数,而t则是isType函数内的t,能够稍微把整个流程体会一下,就差很少明白回调函数是干吗的了~

那么问题就出现了,好比咱们使用node.js的时候,进行文件读取操做时,想获取的值是一种嵌套依赖关系时,会出现什么问题呢?

目录结构:
- iamswr
  - A.txt
  - B.txt
  - C.txt
  
  其中
  A.txt文件里的内容为字符串B.txt
  B.txt文件里的内容为字符串C.txt
  C.txt文件里的内容为字符串'hello swr'
  
  那么当咱们想获取到'hello swr',会遇到什么问题呢?请看下面的代码
  let fs = require('fs')
  fs.readFile('A.txt','utf8',function(err,data){ // 此时回调函数data值为'B.txt'
      fs.readFile(data,'utf8',function(err,data){ // 此时回调函数data值为'C.txt'
          fs.readFile(data,'utf8',function(err,data){
            console.log(data) // 'hello swr'
          })
      })
  })
复制代码

以上这个例子若是嵌套依赖层次更高一些,那代码变得十分难维护以及难阅读,咱们在企业开发当中,常常会遇到想获得的数据,是经过嵌套依赖的关系,最终才得到须要的数据,陷入了回调地狱,而es6中,promise解决了这个让前端头疼的问题,后面我会详细讲promise,下面咱们先了解一下闭包、高阶函数。


二.闭包、高阶函数

我我的理解,闭包其实是一种函数,因此闭包技术也是函数技术的一种;闭包能作的事情函数几乎都能作,闭包有最大的两个用处,一个是能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。

1.封闭做用域

在javascript中,若是一个对象不被引用了,那么这个对象会被GC回收,不然则一直保留在内存中,那么利用这个特色,配合闭包使用,有如下几个优势:封闭做用域、保存做用域、做用域链条。

不污染全局变量,当团队协做时,好比A大佬,封装了jQuery库,而jQuery库内是有大量变量,若是不使用闭包,则jQuery库内的变量会污染整个项目,甚至和其余团员的变量有冲突

外部没法获取闭包内的变量,封闭了做用域
(function(){
    var str = 'hello swr'
    console.log(str) // 'hello swr'
})()

console.log(str) // 报错

咱们用原生js来写代码的时候,会存在一个问题,
好比有5个button标签
var btns = document.getElementsByTagName('button');
for(var i=0; i< btns.length; i++){   
   var btn = btns[i];        
   btn.onclick = function () {            
     alert('点击了第' + i + '个按钮');       
   }
}
不管咱们点击哪一个button,都是弹出'点击了第5个按钮',
由于btn.onclick事件是异步触发的,当事件被触发时,
for循环早已经结束,此时变量I的值已是5,
全部onclick事件函数从内到外查找变量i时,查找到的值老是5。

能够经过封闭做用域把每次循环的i值都封闭起来,
当时间函数顺着做用域链从内到外查找变量i时,
会先找到被封闭在闭包环境中的i,
若是有5个按钮, 则i的值就是0,1,2,3,4
var btns = document.getElementsByTagName('button');
for(var i=0; i< btns.length; i++){   
    (function (i) {        
       var btn = btns[i];        
       btn.onclick = function () {            
         alert('点击了第' + i + '个按钮');       
       }    
     })(i);
}

复制代码

2.做用域链

咱们知道,在es6以前,只有函数是有做用域的说法,在es6出现了,则有了块级做用域的说法,好比

(function person(){
    var name = '邵威儒'
    console.log(name) // '邵威儒'
})()

console.log(name) // 报错
复制代码

在函数外部,是访问不了内部的name,这就是做用域。 在es6出了一个新的概念,就是块级做用域

{
    let name = '邵威儒'
    console.log(name) // '邵威儒'
}

console.log(name) // 报错
复制代码

效果和闭包同样

3.保存做用域

函数嵌套函数,那么内部的那个函数将造成做用域闭包。简单的说,这种闭包可以达到的好处就是让指令可以绑定一些全局数据去运行,优势是全局数据隐藏化、 将数据绑定在指令上运行,让指令再也不依赖全局数据。

function plus(num){
    ++num
    return function(){
        console.log(num)
    }
}

let toPlus = plus(5)
此时toPlus实际上为
function(){
    console.log(num)
}
而这个num实际上就是plus函数内做用域的num,此时咱们没法从外部修改num,并且把plus函数内的数据隐藏化,将数据绑定在toPlus上运行。
复制代码

实际开发中遇到的问题

好比说,咱们实际开发中会遇到一个问题,就是某个函数,要等多个异步执行完毕后才执行,这种状况怎么作呢?

通常会想到如下这个办法

let fs = require('fs')
let arr = []

fs.readFile('./a.txt','utf8',function(err,data){
    arr.push(data) // 假设data为'hello'
})

fs.readFile('./b.txt','utf8',function(err,data){
    arr.push(data) // 假设data为'swr'
})

console.log(arr) // 咱们但愿打印出来是['hello','swr']或['swr','hello'],可是打印出来的倒是[]
这是为何呢?
是由于javascript执行原理,是先执行同步,再执行异步的,而fs.readFile方法属于异步方法,因此还没执行完毕,就已经执行了console.log(arr)了
复制代码

对于这种并不是依赖嵌套获取,咱们称为“同步”获取,此同步非异步同步的那个同步,特别是这种异步请求的数据,获取到的时间前后顺序不一样,那咱们该如何实现“同步”获取呢?

let fs = require('fs')

function after(times,callback){
    let arr = []
    return function(data){
        arr.push(data)
        if(--times === 0){
            callback(arr)
        }
    }
}

let fn = after(2,function(arr){
    console.log(arr) // 当fn执行两次后,则会执行该回调函数
})

fs.readFile('./a.txt','utf8',function(err,data){
    fn(data) // 假设data为'hello'
})

fs.readFile('./b.txt','uft8',function(err,data)=>{
    fn(data) // 假设data为'swr'
})

最终当2个fs.readFile读取完毕后,执行了fn()达到2次时,则会打印出['hello','swr']或者['swr','hello']
复制代码

虽然以上的方式,实现了咱们须要的需求,可是问题来了,难道咱们每一次都要特地写一个after函数吗?其实还有一个概念,叫作发布订阅,订阅就相似你收藏了这个电台,而发布,则是这个电台向全部收藏了本电台的粉丝进行广播,看下面代码

let fs = require('fs')

let event = {
    arr:[], // 存须要执行的函数
    result:[], // 存结果
    on(fn){ // 订阅
        this.arr.push(fn)
    },
    emit(data){ // 发布
        this.result.push(data)
        this.arr.forEach(fn=>fn(this.result))
    }
}

event.on(function(data){
    if(data.length === 2){
        console.log(data) // ['hello','swr'] 或者 ['swr','hello']
    }
})

fs.readFile('./a.txt','utf8',(err,data)=>{
    event.emit(data) // data为'hello'
})

fs.readFile('./b.txt','utf8',(err,data)=>{
    event.emit(data) // data为'swr'
})

当两个fs.readFile读取完成,而且在其回调函数内执行了event.emit,最终会打印出['hello','swr'] 或者 ['swr','hello']
复制代码

三.Promise

啰啰嗦嗦说了那么多,主要是想你们了解一下回调函数以及闭包,由于这概念和promise的紧密关联的,promise部分我主要是想和你们根据promiseAplus规范,逐步手写一个promise的底层实现方式。

首先,promise怎么理解?我在知乎上看到一篇比较通俗易懂的小故事,你们能够看看,zhuanlan.zhihu.com/p/19622332

早上,老爸说:“儿子,天气如何?” 每周一早上,老爸问儿子下午的天气状况,儿子能够到自家房子旁边小山上使用望远镜来观看。儿子在出发时许诺(Promise)老爸(会通知老爸天气状况)。 此刻,老爸决定,若是天气不错,明天就出去捕鱼,不然就不去。并且若是儿子没法得到天气预报的话,也不去捕鱼。 30分钟左右,儿子回来了,每周的结局都不同。

结局A:成功得到了(retrieved)天气预报,晴天 :) 儿子成功获取了天气预报,天空晴朗,阳光明媚!承诺(Promise)兑现了(resolved),因而老爸决定开始为周日的捕鱼作准备。

结局B:一样成功得到了天气预报,雨天:( 儿子成功得到了天气预报,只不过是乌云密布,要下雨。承诺(Promise)兑现了(resolved),只是老爸决定呆在家里,由于天气很糟糕。

结局C:无法得到天气预报:-/ 出了问题,儿子无法得知天气预报,由于雾很大,就算站在小山上也没法看清。儿子没办法对象他离开时许下的诺言, promise was rejected!老爸决定留下来,这并不值得冒险。

Promise的一些特性

首先咱们要了解PromiseA+规范 promisesaplus.com/

  • promise是有兼容性问题的,node环境下默认支持,还能够下载相应插件来解决兼容性问题
  • promise是有三种状态的,等待态pending / 成功态resolved / 失败态rejected
  • promise的状态是能够转换的,能够从pending -> resolved 或 pending -> rejected,可是resolved不能转换为rejected/pending,rejected不能转换为resolved/pending,简而言之即状态只会更改一次
// Promise构造函数的第一个参数为executor
let promise = new Promise(function(resolve,reject){
    console.log('我是会被当即执行的哟')
})

// promise的实例都有then方法
promise.then(()=>{ // 成功的回调
    
},()=>{ // 失败的回调
    
})
复制代码
  • executor默认在new的时候会自动执行
  • 每一个promise的实例都有then方法
  • then方法中,有两个参数,分别是成功的回调函数和失败的回调函数
// 默认时为pending态,既不会走成功的回调也不会走失败的回调
promise.then(()=>{
    console.log('success1')
},()=>{
    console.log('error1')
})

console.log('2')

在这段代码中,只会打印出'2',由于promise一直处于pending态,不会走then后的回调函数
复制代码
let promise = new Promise(function(resolve,reject){
    console.log('1')
    resolve() // 更改pending状态为resolved
})

promise.then(()=>{
    console.log('success1')
},()=>{
    console.log('error1')
})

console.log('2')

此时输出顺序为'1' -> '2' -> 'success1'
复制代码
  • then方法是异步的,属于微任务,从上面的例子能够看出,先执行完同步代码,再执行异步代码
let promise = new Promise(function(resolve,reject){
    console.log('1')
    setTimeout(()=>{ // 异步行为
        resolve() // 更改状态为成功
    },1000)
})

promise.then(()=>{
    console.log("success1")
})

promise.then(()=>{
    console.log('success2')
})

console.log("2")

此时输出顺序为'1' -> '2' -> 'success1' -> 'success2'
复制代码
  • 同一个promise的实例能够then屡次,成功时会调用全部的成功方法,失败时会调用全部的失败方法
  • new Promise中能够支持异步行为
let promise = new Promise(function(resolve,reject){
    throw new Error('出错了') // 抛出错误
})

promise.then(()=>{
    console.log('success1')
},()=>{
    console.log('error1')
})

此时输出为 'error1'
复制代码
  • 若是发现错误,就会进入失败态

实现一个Promise

下面代码部分和源码实现部分要结合来看

// ----- 代码部分
// 1.executor默认在new的时候会自动执行
// 成功和失败的视乎能够传递参数
let promise = new Promise((resolve,reject)=>{ // 6.resolve、reject函数对应源码实现部分的resolve、reject函数
    resolve('hello swr') // 11.执行resolve
})

// 7.Promise的实例都有then方法
promise.then((data)=>{ // 8.成功的回调函数
    
},(err)=>{ // 9.失败的回调函数
    
})
复制代码
// ----- 源码实现部分
// 2.声明一个Promise构造函数
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined // 12.由于value和reason值须要在Promise实例方法then中使用,因此把这两个值,赋给new出来的实例
    function resolve(value){ // 3.声明一个resolve函数
        self.value = value // 13.当调用了resolve而且传参数时,则把这value值赋予self.value
    }
    
    function reject(reason){ // 4.声明一个reject函数
        self.reason = reason // 13.当调用了reject而且传参数时,则把这reason值赋予self.reason
    }
    executor(resolve,reject) // 5.把resolve、reject函数传到executor
}

// 由于Promise的实例都有then方法,那么意味着then方法是在Promise的原型对象中的方法
// 10.对应上面成功的回调函数onFulfilled以及失败的回调函数onRejected
Promise.prototype.then = function(onFulfilled,onRejected){
    
}

module.exports = Promise // 把Promise暴露出去
复制代码

此时,咱们会发现,如何去判断调用resolve仍是reject呢? 这个时候咱们在内部应该维护一个状态,而咱们以前说过了Promise有三种状态,分别为pending、resolved、rejected,那么咱们接着看下面的代码。

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve('hello swr') // 5.暂时忽略此行
    resolve('看看同时执行resolve和reject会发生什么?')  // 5.此行执行resovle
    reject('看看同时执行resolve和reject会发生什么?') // 5.此行执行reject
})

promise.then((data)=>{
    console.log('success:' + data) // 5.当调用了resolve函数,则输出success:hello swr
},(err)=>{
    
})
复制代码
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending' // 1.在内部维护一个status状态
    function resolve(value){
        self.value = value 
        self.status = 'resolved' // 2.当调用了resolve时,更改状态为resolved
    }
    
    function reject(reason){
        self.reason = reason 
        self.status = 'rejected' // 2.当调用了reject时,更改状态为rejected
    }
    executor(resolve,reject)
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    // 3.当咱们在then中,执行了成功或者失败的回调函数时,首先要判断目前处于什么状态
    if(self.status === 'resolved'){
        onFulfilled(self.value) // 4.当调用了resolve函数后,会执行成功的回调函数,而且把resolve中传递的值,传递给成功的回调函数
    }
    
    if(self.status === 'rejected'){
        onRejected(self.reason) // 4.当调用了reject函数后,会执行成功的回调函数,而且把reject中传递的值,传递给失败的回调函数
    }
}

module.exports = Promise
复制代码

当咱们在上面5中同时执行resolve和reject,会发现都可以执行,那么就违背了状态只能更改一次的原则了,下面咱们来解决这个问题。

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve('看看同时执行resolve和reject会发生什么?') // 1. 此时执行resolve和reject
    reject('看看同时执行resolve和reject会发生什么?') // 3.此时即便调用reject,由于resolve已经调用了一次,从pending更改成resolve,因此在第一次调用后,屡次调用也不会生效
    
    // 4.以上resolve、reject暂时忽略掉,咱们考虑一个状况,当promise抛出错误时,怎么去处理呢?
    throw new Error('出错啦')
})

promise.then((data)=>{
    console.log('success:' + data)
},(err)=>{
    
})
复制代码
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    function resolve(value){
        if(self.status === 'pending'){ // 2.此时新增一个状态判断,当状态为pending的时候才能执行
            self.value = value 
            self.status = 'resolved'
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ // 2.此时新增一个状态判断,当状态为pending的时候才能执行
            self.reason = reason 
            self.status = 'rejected'
        }
    }
    
    // 5.当咱们在执行executor时,内部抛出错误的时候,能够利用try catch来处理这个问题
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    if(self.status === 'resolved'){
        onFulfilled(self.value) 
    }
    
    if(self.status === 'rejected'){
        onRejected(self.reason) 
    }
}

module.exports = Promise
复制代码

这样咱们就解决了屡次调用,只认第一次的更改状态,而且当抛出错误时,使用try catch来处理,那么接下来,咱们想一下,目前咱们都是new一个Promise,而后调用then,这整个流程,仿佛没任何问题,可是,如今问题出现了,若是此时resolve或者reject是处于setTimeout(()=>{resolve()},3000)中,即处于异步中,当咱们new一个Promise时,不会立刻执行异步代码,而是直接执行了promise.then这个函数,而此时由于self.status的状态依然是处于pending,因此不会执行resolve或者reject,当同步代码执行完毕后,执行异步代码时,更改了状态为resolved或者rejected时,此时then方法已经执行完毕了,不会再次执行then的方法,那么此时咱们该如何处理?

还存在一个问题,就是上面所说的,同一个promise的实例能够then屡次,成功时会调用全部的成功方法,失败时会调用全部的失败方法,那这个又该如何处理呢?

能够利用咱们前面所说的发布订阅的思路来解决,如今咱们看下面代码。

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    setTimeout(()=>{ // 1.此时resolve处于异步
        resolve('hello swr')
    },3000)
})

promise.then((data)=>{ // 多个then
    console.log('success1:' + data)
},(err)=>{
    
})
promise.then((data)=>{ // 多个then
    console.log('success2:' + data)
},(err)=>{
    
})
复制代码
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    self.onResolvedCallbacks = [] // 2.可能new Promise中会有异步的操做,此时咱们把异步操做时,执行的then函数的成功回调,统一保存在该数组中
    self.onRejectedCallbacks = [] // 2.可能new Promise中会有异步的操做,此时咱们把异步操做时,执行的then函数的失败回调,统一保存在该数组中
    function resolve(value){
        if(self.status === 'pending'){ 
            self.value = value 
            self.status = 'resolved'
            // 4.当调用resolve时,把该数组中存放的成功回调都执行一遍,若是是异步,则会把成功的回调都存到该数组里了,若是是异步,则没存到。
            self.onResolvedCallbacks.forEach(fn=>fn())
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ 
            self.reason = reason 
            self.status = 'rejected'
            // 4.当调用reject时,把该数组中存放的失败回调都执行一遍,若是是异步,则会把成功的回调都存到该数组里了,若是是异步,则没存到。
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    if(self.status === 'resolved'){
        onFulfilled(self.value) 
    }
    
    if(self.status === 'rejected'){
        onRejected(self.reason) 
    }
    
    // 3.当new Promise中有resolve、reject处于异步中,执行then的时候,状态为pending,
    if(self.status === 'pending'){
        self.onResolvedCallbacks.push(()=>{
            onFulfilled(self.value)
        }) // 3. 把成功的回调函数,存到该数组中,这样写的好处,就是把参数传进去,不须要未来遍历onResolvedCallbacks时,再传参
        self.onRejectedCallbacks.push(()=>{
            onRejected(self.reason)
        }) // 3. 把失败的回调函数,存到该数组中,这样写的好处,就是把参数传进去,不须要未来遍历onRejectedCallbacks时,再传参
    }
}

module.exports = Promise
复制代码

到此为止,咱们简版的Promise实现得差很少了,小伙伴们能够对着代码敲一下,感觉一下,体会一下。


Promise的链式调用

其实Promise的核心在于链式调用,Promise主要是解决2个问题:

  • 回调地狱
  • 并发异步io操做,同一时间内把这个结果拿到,即好比有两个异步io操做,当这2个获取完毕后,才执行相应的代码,好比前面所说的after函数,发布订阅、Promise.all等。

首先,好比回调地狱怎么解决呢?那么咱们来看下面的代码,而且改成promise。

// 回调函数
let fs = require('fs')
fs.readFile('./a.txt','utf8',(err,data)=>{ // 往fs.readFile方法传递了第三个为函数的参数
    if(err){
        console.log(err)
        return
    }
    console.log(data)
})

// 改写为Promise
let fs = require('fs')
function read(filePath,encoding){
    return new Promise((resolve,reject)=>{
        fs.readFile(filePath,encoding,(err,data)=>{
            if(err) reject(err)
            resolve()
        })
    })
}

read('./a.txt','utf8').then((data)=>{ // 在这里则再也不须要传回调函数进去,而是采用then来达到链式调用
    console.log(data)
},(err)=>{
    console.log(err)
})

// 这样看好像Promise也没什么优点,那么接下来咱们对比一下
// 假设有3个文件
// - 1.txt    文本内容为'2.txt'
// - 2.txt    文本内容为'3.txt'
// - 3.txt    文本内容为'hello swr'

// 用回调函数
fs.readFile('./1.txt','utf8',(err,data)=>{
    fs.readFile(data,'utf8',(err,data)=>{
        fs.readFile(data,'utf8',(err,data)=>{
            console.log(data) // hello swr
        })
    })
})

// 用Promise
read('./1.txt','utf8')
.then((data)=>{
    // 1.若是一个promise执行完后,返回的仍是一个promise,
    //   会把这个promise的执行结果会传递给下一次thenreturn read(data,'utf8')
})
.then((data)=>{
    return read(data,'utf8')
})
.then((data)=>{
    // 2.若是在then中返回的不是一个promise,
    //   而是一个普通值,会将这个普通值做为下次then的成功的结果
    return data.split('').reverse().join('')
})
.then((data)=>{
    console.log(data) // rws olleh
    // 3.若是当前then中失败了,会走下一个then的失败回调
    throw new Error('出错')
})
.then(null,(err)=>{
    console.log(err) // Error:出错   报错了
    // 4.若是在then中不返回值,虽然没有显式返回,
    //   可是默认是返回undefined,是属于普通值,依然会把这个普通值传到
    //   下一个then的成功回调中
})
.then((data)=>{
    console.log(data) // undefined
})
复制代码

从上面能够看得出,改写为Promise的代码,更好阅读和维护,从用Promise方式能够得出结论:

  • 1.若是一个promise执行完后,返回的仍是一个promise,会把这个promise的执行结果会传递给下一次then中
  • 2.若是在then中返回的不是一个promise,而是一个普通值,会将这个普通值做为下次then的成功的结果
  • 3.若是当前then中失败了,会走下一个then的失败回调
  • 4.若是在then中不返回值,虽然没有显式返回,可是默认是返回undefined,是属于普通值,依然会把这个普通值传到下一个then的成功回调中
// 若是在then中抛出错误,会怎样呢?
// 情景一,会被下一个then中的失败回调捕获
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})
.then(null,(err)=>{
    console.log(err) // Error:出错了   报错
})

// 情景二,若是没有被失败的回调捕获,抛出错误最终会变成异常
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})

// 情景三,若是没有被失败的回调捕获,那么最终会被catch捕获到
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})
.then((data)=>{
    
})
.catch((err)=>{
    console.log(err) // Error:出错了   报错
})

// 情景四,若是被失败的回调捕获了,那么不会被catch捕获到
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})
.then(null,(err)=>{
    console.log(err) // Error:出错了   报错
})
.catch((err)=>{
    console.log(err)  // 不会执行到这里
})
复制代码
  • 5.catch是错误没有处理的状况下才会执行
  • 6.then中能够不写东西

穿插一个与jquery的链式调用区别

jquery的链式调用,是经过其内部执行完后return this,返回自身这个对象,达到链式调用的目的,那为何Promise不采用这种方式呢?

咱们能够看如下代码,感觉一下。

let promise = new Promise((resolve,reject)=>{
    resolve() // 执行resolve,使状态从pending变为resolved
})

let promise2 = promise.then(()=>{
    throw new Error() // 抛出错误
    return this // 返回自身
})

// 那么我在promise2中,调then,那么它会执行失败的回调吗?答案是不会的。
// 由于咱们不可能让状态既成功又失败的
// promise成功了,若是返回this,那不能走向失败
promise2.then(()=>{
    console.log('来到这里了') 
},()=>{
    console.log('会来到这里吗?')
})

// 此时then中返回自身后,promise2其实就是promise,而咱们想达到
// 的是把当前的then返回后,传到下一个then中,可是咱们这样返回this,
// 其实会变得很矛盾,由于状态已经从pending变为resolved,不可能又从resolved变成rejected的
// 因此得出结论,返回的必须是一个新的promise,由于promise成功后不能再走失败
// 只能建立一个新的promise再执行业务逻辑,返回同一个promise的话,就不能既成功又失败
复制代码
实现Promise链式调用
// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve()
})

// 2.返回的值为promise2 为何这样规定呢?这是promiseA+规范规定的,咱们要遵循
let promise2 = promise.then((data)=>{
    return x // 1.then中的返回值x多是普通值也多是promise,而且传给下一个then
}).then((data)=>{
    console.log(data) // x的值
})
复制代码
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    self.onResolvedCallbacks = []  
    self.onRejectedCallbacks = []
    function resolve(value){
        if(self.status === 'pending'){ 
            self.value = value 
            self.status = 'resolved'
            self.onResolvedCallbacks.forEach(fn=>fn())
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ 
            self.reason = reason 
            self.status = 'rejected'
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    let promise2 // 3.上面讲promise链式调用时,已经说了返回的是一个新的promise对象,那么咱们声明一个新的promise
    
    // 4.那么咱们new一个新的promise,而且把如下代码放到promise中
    let promise2 = new Promise((resolve,reject)=>{
        if(self.status === 'resolved'){
            // 7.当执行成功回调的时候,可能会出现异常,那么就把这个异常做为promise2的错误的结果
            try{
                let x = onFulfilled(self.value) // 6.这里的x,就是上面then中执行完返回的结果,咱们在这里声明一个x用来接收
                // 8.根据promiseA+规范,咱们应该提供一个函数来处理promise2
                //   我我的的理解是,then中无论是成功回调仍是失败回调,其返回
                //   值,有多是promise,也有多是普通值,也有多是抛出错误
                //   那么咱们就须要一个函数来处理这几种不一样的状况
                //   这个函数咱们声明为resolvePromise吧
                resolvePromise(promise2,x,resolve,reject)
                // 9. 这里的promise2就是当前的promise2,x则是执行then中成功回调后返回的结果,若是是成功则调promise2的resolve,失败则调reject
            }catch(e){
                reject(e) // 注意:这里的reject是这个promise2的reject
            }
        }
        
        if(self.status === 'rejected'){
            // 同6-7步
            try{
                let x = onRejected(self.reason) 
                // 同8-9
                resolvePromise(promise2,x,resolve,reject)
            }catch(e){
                reject(e)
            }
        }
    
        if(self.status === 'pending'){
            self.onResolvedCallbacks.push(()=>{
                // 同6-7步
                try{
                    let x =  onFulfilled(self.value)
                    // 同8-9
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            }) 
            self.onRejectedCallbacks.push(()=>{
                // 同6-7步
                try{
                    let x = onRejected(self.reason)
                    // 同8-9
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            })
        }
    })
    return promise2 // 5.在jquery中是return this,可是在promise中,则是返回一个新的promise对象
}

module.exports = Promise
复制代码

写一个resolvePromise函数

接下来咱们写一下resolvePromise这个函数,整个Promise最核心的部分就是在这里

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve()
})

let promise2 = promise.then((data)=>{
    return x 
}).then((data)=>{
    console.log(data) 
})

// 2.咱们在resolvePromise函数中,在原生状况下,若是传参的时候,promise2和x是同一个对象会发生什么呢?
let promise = new Promise((resolve,reject)=>{
    resolve()
})

let promise2 = promise.then(()=>{
    return promise2 
    // 2.1报错 UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>
    // 报错的意思是,陷入了死循环,那怎么理解呢?
    // promise2的成功或失败是要取决于promise中then的返回结果,而返回的倒是promi2本身
    // 这样就陷入死循环了,promise2是依赖于promise的then返回的结果,
    // 而then返回的结果是promise2,而then中的promise2,既不是成功也不是失败,不能本身等于本身
})

// 7.当取一个对象上的属性,可能存在报异常的状况,怎么理解呢?
// 由于这个方法有可能不是本身写的,可能别人搞恶做剧乱写的,看如下代码。
let obj = {}
// 给obj对象定义一个then方法,当咱们去obj对象中调用then方法时
// 就会执行里面的get,而get则是抛出异常
Object.defineProperty(obj,'then',{
    get(){
        throw new Error()
    }
})

// 10.为何要用call呢?解决了什么问题?看一下如下代码
首先咱们执行
promise.then(()=>{
    console.log(this) // 此时this是指向该promise的,对象的方法中this是指向这个对象的
})

可是咱们在下面经过let then = promise.then,来判断是否promise,是否会异常
当咱们执行then时,里面的this仍是会指向这个promise吗?答案是不必定的,
由于此时then,若是在全局下执行,指向的可能就是window了,因此为了让this的
指向正确,咱们须要经过
then.call(promise),来把then的this指向promise
复制代码
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    self.onResolvedCallbacks = []  
    self.onRejectedCallbacks = []
    function resolve(value){
        if(self.status === 'pending'){ 
            self.value = value 
            self.status = 'resolved'
            self.onResolvedCallbacks.forEach(fn=>fn())
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ 
            self.reason = reason 
            self.status = 'rejected'
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

// 1.声明一个resolvePromise函数
// 这个函数很是核心,全部的promise都遵循这个规范,全部的promise能够通用,
/**
 * 
 * @param {*} promise2 then的返回值,返回新的promise
 * @param {*} x then中成功函数或者失败函数的返回值
 * @param {*} resolve promise2的resolve
 * @param {*} reject promise2的reject
 */
function resolvePromise(promise2,x,resolve,reject){
    // 3.从2中咱们能够得出,本身不能等于本身
    // 当promise2和x是同一个对象的时候,则走reject
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    // 4.由于then中的返回值能够为promise,当x为对象或者函数,才有可能返回的是promise
    let called
    if(x !== null && (typeof x === 'object' || typeof x === 'function')){
        // 8.从第7步,能够看出为何会存在抛出异常的可能,因此使用try catch处理
        try{
            // 6.由于当x为promise的话,是存在then方法的
            // 可是咱们取一个对象上的属性,也有可能出现异常,咱们能够看一下第7步
            let then = x.then 
            
            // 9.咱们为何在这里用call呢?解决了什么问题呢?能够看上面的第10步
            // x可能仍是个promise,那么就让这个promise执行
            // 可是仍是存在一个恶做剧的状况,就是{then:{}}
            // 此时须要新增一个判断then是否函数
            if(typeof === 'function'){
                then.call(x,(y)=>{ // y是返回promise后的成功结果
                    // 一开始咱们在这里写的是resolve(y),可是考虑到一点
                    // 这个y,有可能仍是一个promise,
                    // 也就是说resolve(new Promise(...))
                    // 因此涉及到递归,咱们把resolve(y)改为如下
                    
                    // 12.限制既调resolve,也调reject
                    if(called) return
                    called = true
                    
                    resolvePromise(promise2,y,resolve,reject)
                    // 这样的话,代码会一直递归,取到最后一层promise
                    
                    // 11.这里有一种状况,就是不能既调成功也调失败,只能挑一次,
                    // 可是咱们前面不是处理过这个状况了吗?
                    // 理论上是这样的,可是咱们前面也说了,resolvePromise这个函数
                    // 是全部promise通用的,也能够是别人写的promise,若是别人
                    // 的promise可能既会调resolve也会调reject,那么就会出问题了,因此咱们接下来要
                    // 作一下限制,这个咱们写在第12步
                    
                },(err)=>{ // err是返回promise后的失败结果
                    if(called) return
                    called = true
                    reject(err)
                })
            }else{
                resolve(x) // 若是then不是函数的话,那么则是普通对象,直接走resolve成功
            }
        }catch(e){ // 当出现异常则直接走reject失败
            if(called) return
            called = true
            reject(e)
        }
    }else{ // 5.x为一个常量,则是走resolve成功
        resolve(x)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    // onFulfilled、onRejected是可选参数
    onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
    onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
    let self = this
    let promise2 
    let promise2 = new Promise((resolve,reject)=>{
        if(self.status === 'resolved'){
            // 13.根据promiseA+规范,onFulfilled或onRejected必须
            // 被调用不是当前的上下文,then方法是异步的
            setTimeout(()=>{
                try{
                    let x = onFulfilled(self.value)
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e) 
                }
            },0)
        }
        
        if(self.status === 'rejected'){
            // 同13
            setTimeout(()=>{
                try{
                    let x = onRejected(self.reason) 
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            },0)
        }
    
        if(self.status === 'pending'){
            self.onResolvedCallbacks.push(()=>{
                // 同13
                setTimeout(()=>{
                    try{
                        let x =  onFulfilled(self.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }) 
            self.onRejectedCallbacks.push(()=>{
                // 同13
                setTimeout(()=>{
                    try{
                        let x = onRejected(self.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            })
        }
    })
    return promise2
}

// 14.到目前为止,根据promiseA+规范的代码写得差很少了,咱们能够经过测试代码来测试咱们是否写得正确,下面咱们写一段测试代码

Promise.defer = Promise.deferred = function(){
    let dfd = {}
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}

// 14.接下来咱们要安装一个插件,npm install promises-aplus-test -g

module.exports = Promise
复制代码
// 完整代码 也顺便带你们理顺一下
function Promise(executor) {
    let self = this;
    self.value = undefined;  // 成功的值
    self.reason = undefined;  // 失败的值
    self.status = 'pending'; // 目前promise的状态pending
    self.onResolvedCallbacks = []; // 可能new Promise的时候会存在异步操做,把成功和失败的回调保存起来
    self.onRejectedCallbacks = [];
    function resolve(value) { // 把状态更改成成功
        if (self.status === 'pending') { // 只有在pending的状态才能转为成功态
            self.value = value;
            self.status = 'resolved';
            self.onResolvedCallbacks.forEach(fn => fn()); // 把new Promise时异步操做,存在的成功回调保存起来
        }
    }
    function reject(reason) {  // 把状态更改成失败
        if (self.status === 'pending') { // 只有在pending的状态才能转为失败态
            self.reason = reason;
            self.status = 'rejected';
            self.onRejectedCallbacks.forEach(fn => fn()); // 把new Promise时异步操做,存在的失败回调保存起来
        }
    }
    try {
        // 在new Promise的时候,当即执行的函数,称为执行器
        executor(resolve, reject);
    } catch (e) { // 若是执行executor抛出错误,则会走失败reject
        reject(e);
    }
}

// 这个函数为核心,全部的promise都遵循这个规范
// 主要是处理then中返回的值x和promise2的关系
function resolvePromise(promise2,x,resolve,reject){
    // 当promise2和then返回的值x为同一个对象时,变成了本身等本身,会陷入死循环
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle'));
    }
    let called;
    // x多是一个promise也多是一个普通值
    if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
        try{
            let then = x.then; 
            if(typeof then === 'function'){
                then.call(x,y=>{ 
                    if(called) return; 
                    called = true;
                    resolvePromise(promise2,y,resolve,reject);
                },err=>{ 
                    if(called) return;
                    called = true;
                    reject(err);
                });
            }else{
                resolve(x);
            }
        }catch(e){
            if(called) return;
            called = true;
            reject(e);
        }
    }else{ 
        resolve(x);
    }
}

// then调用的时候,都是属于异步,是一个微任务
// 微任务会比宏任务先执行
// onFulfilled为成功的回调,onRejected为失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
    onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
    let self = this;
    let promise2;
    // 上面讲了,promise和jquery的区别,promise不能单纯返回自身,
    // 而是每次都是返回一个新的promise,才能够实现链式调用,
    // 由于同一个promise的pending resolve reject只能更改一次
    promise2 = new Promise((resolve, reject) => {
        if (self.status === 'resolved') {
            // 为何要加setTimeout?
            // 首先是promiseA+规范要求的
            // 其次是你们写的代码,有的是同步,有的是异步
            // 因此为了更加统一,就使用为setTimeout变为异步了,保持一致性
            setTimeout(()=>{
                try { // 上面executor虽然使用try catch捕捉错误
                      // 可是在异步中,不必定可以捕捉,因此在这里
                      // 用try catch捕捉
                    let x = onFulfilled(self.value);
                    // 在then中,返回值多是一个promise,因此
                    // 须要resolvePromise对返回值进行判断
                    resolvePromise(promise2,x,resolve,reject);
                } catch (e) {
                    reject(e);
                }
            },0)
        }
        if (self.status === 'rejected') {
            setTimeout(()=>{
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                } catch (e) {
                    reject(e);
                }
            },0)
        }
        if (self.status === 'pending') {
            self.onResolvedCallbacks.push(() => {
                setTimeout(()=>{
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                },0)
            });
            self.onRejectedCallbacks.push(() => {
                setTimeout(()=>{
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                },0)
            });
        }
    });
    return promise2
}
Promise.defer = Promise.deferred = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = Promise;
复制代码
执行promises-aplus-tests promise.js
复制代码

到此为止,咱们已经写了一个符合promiseA+规范的promise了,你们能够好好多看几回。


接下来,咱们完善一下这个promise,写一下经常使用的promise方法

  • Promise.reject
  • Promise.resolve
  • catch
  • Promise.all
  • Promise.race
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason);
    })
}

Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{
        resolve(value);
    })
}

Promise.prototype.catch = function(onRejected){
    return this.then(null,onRejected);
};

Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let arr = [];
        let i = 0;
        function processData(index,data){
            arr[index] = data;
            if(++i == promises.length){
                resolve(arr);
            }
        }
        for(let i = 0;i<promises.length;i++){
            promises[i].then(data=>{
                processData(i,data);
            },reject);
        }
    })
}

Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0;i<promises.length;i++){
            promises[i].then(resolve,reject);
        }
    })
}
复制代码

Promise的经常使用方法如何实现呢?

Promise.resolve / Promise.reject
// 原生的Promise.resolve使用
Promise.resolve('hello swr').then((data)=>{ // 直接把成功的值传递给下一个then
    console.log(data) // hello swr
})

// 那么Promise.resolve内部是怎么实现的呢?
Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{ // 在内部new一个Promise对象
       resolve(value) 
    })
}

// 同理,Promise.reject内部也是相似实现的
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}
复制代码
catch是怎样实现呢?
// 原生Promise的catch使用
Promise.reject('hello swr').catch((e)=>{
    console.log(e) // hello swr
})

// 上面这段代码至关于下面这段代码
Promise.reject('hello swr').then(null,(e)=>{ // then里直接走了失败的回调
    console.log(e) // hello swr
})

// 内部实现
Promise.prototype.catch = function(onRejected){
    return this.then(null,onRejected) // 至关于then里的成功回调只传个null
}


复制代码
Promise.all,这个方法很是重要,同时执行多个异步,而且返回一个新的promise,成功的值是一个数组,该数组成员的顺序是传参给Promise.all的顺序
// 原生Promise.all的使用
// 假设1.txt内容为hello 2.txt内容为swr
let fs = require('fs')
function read(filePath,encoding){
    return new Promise((resolve,reject)=>{ 
        fs.readFile(filePath,encoding,(err,data)=>{
            if(err) reject(err)
            resolve(data)
        })
    })
}

Promise.all([read('./1.txt','utf8'),read('./2.txt','utf8')]).then((data)=>{
    console.log(data) // 所有读取成功后返回 ['hello','swr']
                      // 须要注意的是,当其中某个失败的话,则会走失败的回调函数
})

// 内部实现
Promise.all = function(promises){ // promises 是一个数组
    return new Promise((resolve,reject)=>{
        let arr = []
        let i = 0
        function processData(index,data){
            arr[index] = data
            // 5.咱们能用arr.length === promises.length来判断请求是否所有完成吗?
            // 答案是不行的,假设arr[2] = 'hello swr'
            // 那么打印这个arr,将是[empty × 2, "hello swr"],
            // 此时数组长度也是为3,而数组arr[0] arr[1]则为空
            // 那么换成如下的办法
            if(++i === promises.length){ // 6.利用i自增来判断是否都成功执行
                resolve(arr) // 此时arr 为['hello','swr']
            }
        }
        
        for(let i = 0;i < promises.length;i++){ // 1.在此处遍历执行
            promises[i].then((data)=>{ // 2.data是成功后返回的结果
                processData(i,data) // 4.由于Promise.all最终返回的是一个数组成员按照顺序排序的数组
                                    // 并且异步执行,返回并不必定按照顺序
                                    // 因此须要传当前的i
            },reject) // 3.若是其中有一个失败的话,则调用reject
        }
    })
}
复制代码
Promise.race 该方法是同时执行多个异步,而后哪一个快,就用哪一个的结果,race的意思是赛跑
// 原生Promise.race的使用
// 一个成功就走成功的回调,一个失败就走失败的回调
Promise.race([read('./1.txt','utf8'),read('./2.txt','utf8')]).then((data)=>{
    console.log(data) // 可能返回 'hello' 也可能返回 'swr' 看哪一个返回快就用哪一个做为结果
})

// 内部实现
Promise.race = function(promises){ // promises 是一个数组
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < promises.length;i++){ 
            promises[i].then(resolve,reject) // 和上面Promise.all有点相似
        }
    })
}
复制代码

Promise.defer = Promise.deferred 这个语法糖怎么理解呢?

这个语法糖能够简化一些操做,好比

let fs = require('fs')
// 写法一:
function read(filePath,encoding){
    // 这里的new Promise依然是传递了一个executor回调函数
    // 咱们该怎样减小回调函数嵌套呢?
    return new Promise((resolve,reject)=>{ 
        fs.readFile(filePath,encoding,(err,data)=>{
            if(err) reject(err)
            resolve(data)
        })
    })
}

// 写法二:
// 这样的写法减小了一层回调函数的嵌套
function read(filePath,encoding){
    let dfd = Promise.defer()
    fs.readFile(filePath,encoding,(err,data)=>{
        if(err) dfd.reject(err)
        dfd.resolve(data)
    })
    return dfd.promise
}

read('./1.txt','utf8').then((data)=>{
    console.log(data)
})


复制代码

结尾:第一次写,都是想到哪写到哪,请你们多多谅解~ 也但愿对你们有所帮助,promise的源码实现,我最大的收获并非怎么实现promise,而是编程思惟,你们能够多多往深里想想,也但愿你们能够和我进行交流,共同进步

相关文章
相关标签/搜索