从0 到熟悉 之 Promise

326 阅读1分钟

promise是什么

  • 首先promise是一种解决异步方案
  • Promise-ES6
  • 获取数据 koa generator async await axios redux-saga fetch 都是基于promise
  • Promise是一种异步流程的控制手段

为什么存在promise

针对在开发中异步需求,例如

//同步异步请求的需求
function request(content){
    setTimeout((){
        document.body.append(content)
    },Math.floor(Math.random()*1000))
}
request('hello')
request('world')
document.body.append('finished');

//回调地狱
$.ajax(
    ...
    sucess:{
        $.ajax(
            ...
            sucess:{
               $.ajax(
                    ...
                    sucess:{
                        
                    }
                ) 
            }
        )
    }
)

假设我在读取到1.txt文件之后,读取2.txt文件,一般我们会嵌套回调,在我们有多个连续读取文件时,就会形成类似于金字塔结构的回调,我们称之为回调地狱。如果有异步挂掉,我们很难排除错误,会使得很不好维护,而且不直观。我们产生了很多种解决异步的方法

接下来我们将会介绍如下

  • 高阶函数,函数是一等公民,函数可以当作参数传给另一个函数,也可以返回函数,例如,偏函数,函数柯里化 ;callback函数
  • Promise
  • generator + co 基于Promise
  • async + await 让promise看起来更像同步

(终极)(写起来越来越像同步代码)

(promise的链式调用和JQuery的不同,他返回的不是this,而是一个新的promise) Promise类上提供类很多方法,

高阶函数

函数可以当作参数传递,典型的callback 像loadash库中的after都是这个样子

那么他是怎么实现的呢?

假设我们吃饭,吃饭是一个函数,调用3次后,在执行另一个函数,代码如下:

function after(times,callback){
    return function(){
        if(--times === 0){
            callback()
        }
    }
}
let eat = after(3,function(){
    console.log('吃完了')
})

输出结果:

图片1

promise

promise是怎么来的呢?为什么会有promise?,假设我要读取两个文件,我们一般会这么写:

let fs = require('fs');// fileSystem
fs.readFile('1.txt','utf8',function(err,data){
    if(err) return console.log(err);
    console.log(data)
    fs.readFile('2.txt','utf8',function(err,data){
    if(err) return console.log(err);
    console.log(data)
    })
})

那么这两个熟从顺序执行,如果我们想同时读取两个文件,又想让两个文件见分别异步读取,那么我们怎么判断读取完毕呢?像上面的做法肯定不行,我们可以如下

let fs = require('fs');// fileSystem
let arr = [];
fs.readFile('1.txt','utf8',function(err,data){
    if(err) return console.log(err);
    out(data)
})
fs.readFile('2.txt','utf8',function(err,data){
    if(err) return console.log(err);
    out(data)
})
function out(d){
    arr.push(d);
    if(arr.length == 2) console.log(arr)
}

这样的话我们在外部声明了一个变量,我们希望arr放到里面,在达到某种条件在执行这个方法,我们可以更具刚开始讲的after进一步优化代码如下:

let fs = require('fs');// fileSystem
fs.readFile('1.txt','utf8',function(err,data){
    if(err) return console.log(err);
    out(data)
})
fs.readFile('2.txt','utf8',function(err,data){
    if(err) return console.log(err);
    out(data)
})
function after(times,callback){
    let arr = [];
    return function(d){
        arr.push(d);
        if(arr.length == times) callback(arr)
    }
}
let out = after(2,function(data){
    console.log(data)
})

ok,用promise如何实现?具体请参考promise/A+规范,下面我们慢慢介绍:

  • promise 是个一个内置类,在promise里面由一个excutor执行器,默认new式直接调用,执行器有两个参数,resolve,和reject
  • 每个promise实例上都有then方法,每个then方法都有成功的函数和失败的函数
  • promise发生错误就走失败态,什么时候成功,什么时候失败,取决于你什么时候调用这个函数,
  • 当然我们每次执行,只能调用其中一个函数,所以promise大致结构已经出来如下:

var Promise = require('./promise.js')
let p = new Promise((resolve,reject)=>{
    resolve();
})
p.then((data)=>{
    console.log('s',data);
},(err)=>{
    console.log('e',err);
})


promise.js

class Promise{
    constructor(executor){ //executor执行器
        this.status = 'pending';//默认会有三个状态
        this.value = undefined;
        this.reason = undefined;
        let resolve = (data)=> {
            if(this.status == 'pending'){//判定只有pending状态可以改变状态
                this.status = 'resolved';//成功之后改变状态
                this.value = data;
            }
        }
        let reject = (reason)=> {
            console.log(reason+'2')
            if(this.status == 'pending'){
                this.status = 'rejected';//失败之后改变失败状态
                this.reason = reason;
            }
        }
        try{//执行时可能会发生异常
            executor(resolve,reject)//默认new调用,有两个函数作为参数
        }catch(e){//如果捕获到异常直接走失败态
            reject(e)
        }
       
    }
    then(onFullFiled,onRejected){//then 里面由两个状态
        if(this.status === 'resolved'){
            console.log(333)
            onFulFilled(this.value)//成功将成功的参数传进去
        }
        if(this.status === 'rejected'){
            onRejected(this.reason)//失败将失败的参数传进去
        }
    }
}
module.exports = Promise;
  • 在Promise里会有很多种情况例如Promise实例的函数是异步的
let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('123');
    },1000)
})
  • 此时我们的状态还没有改变,then的时候status = > pending 状态,此时我们需要保留当前函数,当状态改变的时候在执行,我们知道,promise可能会有n个实例,可以then多次,所以当前存放成功回调和失败回调函数方法的一定是数组,我们在then里多加一个pendding状态,这里利用发布订阅思想,代码如下,
class Promise{
    constructor(executor){
        ...
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        ...
        }
        ...
    }
    then(onFullFiled,onRejected){//then 里面由两个状态
        ...
        if(this.status === 'pending'){//当前既没有完成也没有失败
            //我们可以现将当前两个函数存起来,当执行完成后执行当前两个函数
            this.onResolvedCallbacks.push(()=>{
                onFulFilled(this.value)
            })
            this.onRejectedCallbacks.push(()=>{
                onRejected(this.reason)
            })
        }
    }
}
module.exports = Promise;
  • then可以链式调用,如果第一个then报错,就返回失败态,走到下一个then的失败里去;如果第一个then是成功的,就会返回成功态,就走到下一个then的成功里去,并且将返回结果传过去。那么我们推测
  • 每次then之后返回的都是一个promise
  • 返回成功态,就走到下一个then的成功里去,并且将返回结果传过去
  • 第一个then报错,就返回失败态,走到下一个then的失败里去并且将返回结果传过去
  • 如返回的是普通值,直接把值作为外层的下一次then的函数
  • 由于这里的情况较多,且复杂,我会尽力标注清除,以js备注为准

代码如下

class Promise{
    constructor(executor){...}
    then(onFulFilled,onRejected){//then 里面由两个状态
        let promise2;
        if(this.status === 'resolved'){
            promise2 = new Promise((resolve,reject)=>{//调用then后返回一个新的promise
                let x = onFulFilled(this.value)//成功将成功的参数传进去
                resolvePromise(promise2,x,resolve,reject);
            })
            return promise2;
            //每次return 的值我们要对 这个值做处理,重点就在于这个值x有很多种情况,x可能是值,function,obj,等,我们要对这个作出分析,来判定我们即将要做什么,因为在then的每一种情况,我们都要处理这种关系,所以我们定义一个函数,来检查并继续相应的操作;
            //=> 如果x和promise2是同一个对象,那么会一直处于等待中,我们会跑出一个类型错误
            //=> 如果是 值 直接resolve(x);
            //=> 如果是 function或者 Object 我们认为这就是个promise,返回结果作为外层下次then的函数
            // 这个promise也会分几种情况
            //Object.defineProperty(x,'then',{
                //get(){
                    //throw new Error()
                // },
                // set(){
                // }
            //})
            // ===> 如果用户通过defineProperty,使x的值弹出错误,我们需要捕获错误
            // ===> 如果是 值
            // ===> 如果是 值
        }
        if(this.status === 'rejected'){...}
        //既没有成功也没有失败
        if(this.status === 'pending'){//当前既没有完成也没有失败
            promise2 = new Promise((resolve,reject)=>{...})
            return promise2;
    }
}
module.exports = Promise;
  • 根据以上总结我们来写一下resolvePromise()的实现
  • 如果promise2和x引用同一个object,会抛出一个引用类型错误
//解析promise和promise2的关系
function resolvePromise(promise2,x,resolve,reject){
    //判断x是不是promise
    //规范里规定来一段代码,这个代码规定我的promise可以和别人的promise可以进行交互
    //如果promise2和x引用同一个object,会抛出一个引用类型错误
    if(promise2 === x){//不能自己等待自己完成
        throw reject(new TypeError('循环引用'));
    }
    //如果x是一个对象或者函数(object/function)有可能是promise 
    if(x !== null && (typeof x === 'object' ||typeof x === 'function')){//type of null === Object
        try{//如果用户通过defineProperty,防止then异常
            let then = x.then ;//取x的then {then:{}} 29S
            if(typeof then === 'function'){//如果then是函数,就认为他是个promise,
                then.call(x,y => { //resolve的结果可能依旧是promise,,要继续递归,解析promise
                    resolvePromise(promise2,y,resolve,reject);
                },err => {
                    reject(err)//只要失败就失败了
                })//call(x)相当于让then执行,
            }else{//如果不是函数就是普通值
                resolve(err)//then是一个普通对象,成功即可
            }
        }catch(e){
            reject(e)
        }
    }else{//否则结果是个普通的值,直接进入成功态
        resolve(x)
    }
}

测试

let Promise = require('./a.js');
let p = new Promise((resolve,reject)=>{
   resolve(123)
})
var p2 = p.then((data) => {
    return p2 //返回的promise既不可能成功也不会失败
 })
 p2.then((data) => {
    console.log('p1data',data);
 },(err)=> {
     console.log(err)
 })

测试结果

测试

let Promise = require('./a.js');
let p = new Promise((resolve,reject)=>{
   resolve(123)
})
p.then( data => {
    return new Promise((resolve,reject)=>{
        resolve(new Promise((resolve,reject)=>{
            resolve(100)
        }))
    })
},err => {
    console.log('p',err);
}).then((data)=>{
    console.log('sf',data);
},(err)=>{
    console.log('perr',err);
})

测试结果

多个promise返回测试结果

经过测试基本功能已经实现,但是如果成功就不能失败,失败就能成功,我们怎么限制他只能调一次呢?我们可以加个参数

function resolvePromise(promise2, x, resolve, reject) {
    // 判断x是不是promise
    // 规范里规定了一段代码,这个代码可以实现我们的promise和别人的promise可以进行交互
    if (promise2 === x) { // 不能自己等待自己完成
      return reject(new TypeError('循环引用'));
    }
    // x不是null或者是对象或者函数
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      let called; // 防止成功后调用失败
      try { // 防止取then是出现异常 Object.defineProperty
        let then = x.then; // 取x的then方法 {then:{}}
        if (typeof then === 'function') { // 如果then是函数我就认为它是promise
          // call 第一个参数是this ,后面的是成功的回调和失败的回调
          then.call(x, y => { // 如果y是promise就继续递归解析promise
            if(called) return;
            called = true;
            resolvePromise(promise2,y,resolve,reject);
          }, r => { // 只要失败了就失败了
            if (called) return;
            called = true;
            reject(r);
          });
        }else{ // then是一个普通对象,就直接成功即可1
          resolve(x);
        }
      } catch (e) {
        if (called) return;
        called = true;
        reject(e);
      }
    } else { // x = 123
      resolve(x); // x就是一个普通值
    }
  }

那如果then里面什么也没写,我们需要做一个值的穿透

p.then().then((y) => {

},e => {
    console.log(e)
})

js

p.then().then((y) => {

},e => {
    console.log(e)
})

当我们的实例中放的是异步代码

then(onFulFilled,onRejected){//then 里面由两个状态
        //解决onfullFilled不传值的问题
        onFulFilled = typeof onFulFilled === 'function'? onFulFilled:y => y;
        onRejected = typeof onRejected === 'function'? onRejected: err => {
            throw err;//不能new
        };
        ...
}

但是这样的话,reject会走到成功,所以在reject的时候我们应该抛出错误,而且值是异步的,我们需要try,catch捕获,excutor执行的时候我们try catach,但是内容是异步的就无法捕获错误,所以我们需要给每个then中的方法,都加try,catch

 then(onFulFilled,onRejected){//then 里面由两个状态
        //解决onfullFilled不传值的问题
        onFulFilled = typeof onFulFilled === 'function'? onFulFilled:y => y;
        onRejected = typeof onRejected === 'function'? onRejected:err => {
            throw err;//不能new
        };
        let promise2;
        if(this.status === 'resolved'){
            promise2 = new Promise((resolve,reject)=>{//调用then后返回一个新的promise
                try{
                    let x = onFulFilled(this.value)//成功将成功的参数传进去
                //看x是不是promise,如果是,取结果,作为promise2成功的结果
                //如返回的是普通值作为promise2成功的结果
                //如果成功或者失败,promise2的成功或者失败会被调用
                //这里我们对以上做统一处理
                    resolvePromise(promise2,x,resolve,reject);
                }catch(e){
                    reject(e)
                }
            })
            return promise2;
        }
        if(this.status === 'rejected'){
            promise2 = new Promise((resolve,reject)=>{
                try{
                    let x =  onRejected(this.reason)//失败将失败的参数传进去
                    resolvePromise(promise2,x,resolve,reject);
                }catch(e){
                    reject(e)
                }
            })
            return promise2;
        }
        //既没有成功也没有失败
        if(this.status === 'pending'){//当前既没有完成也没有失败
            promise2 = new Promise((resolve,reject)=>{
                //我们可以现将当前两个函数存起来,当执行完成后执行当前两个函数
                this.onResolvedCallbacks.push(()=>{
                    try{
                        let x =  onFulFilled(this.value);
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e)
                    }
                })
                this.onRejectedCallbacks.push(()=>{
                    try{
                        let x =  onRejected(this.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e)
                    }
                   
                })
            })
            return promise2;
        }
    }

我们知道promise中的then是异步的,而且他是微任务,但是现实中,浏览器有例如setTimeout这样异步的宏任务,可以考虑用,所以我们这次暂时用setTimeout实现异步:

  then(onFulFilled,onRejected){//then 里面由两个状态
        //解决onfullFilled不传值的问题
        onFulFilled = typeof onFulFilled === 'function'? onFulFilled:y => y;
        onRejected = typeof onRejected === 'function'? onRejected:err => {
            throw err;//不能new
        };
        let promise2;
        if(this.status === 'resolved'){
            promise2 = new Promise((resolve,reject)=>{//调用then后返回一个新的promise
                setTimeout(() => {
                    try{
                        let x = onFulFilled(this.value)//成功将成功的参数传进去
                    //看x是不是promise,如果是,取结果,作为promise2成功的结果
                    //如返回的是普通值作为promise2成功的结果
                    //如果成功或者失败,promise2的成功或者失败会被调用
                    //这里我们对以上做统一处理
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e)
                    }
                },0)
            })
        }
        if(this.status === 'rejected'){
            promise2 = new Promise((resolve,reject)=>{
                setTimeout(() => {
                    try{
                        let x =  onRejected(this.reason)//失败将失败的参数传进去
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e)
                    }
                },0)
            })
        }
        //既没有成功也没有失败
        if(this.status === 'pending'){//当前既没有完成也没有失败
            promise2 = new Promise((resolve,reject)=>{
                //我们可以现将当前两个函数存起来,当执行完成后执行当前两个函数
                this.onResolvedCallback.push(()=>{
                    setTimeout(() => {
                        try{
                            let x =  onFullFilled(this.value);
                            resolvePromise(promise2,x,resolve,reject);
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(() => {
                        try{
                            let x =  onRejected(this.reason);
                            resolvePromise(promise2,x,resolve,reject);
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
            })
        }
        return promise2;
    }

这样我们的Promise类就完成了

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('循环引用'));
  }
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called;
    try { // 防止取then是出现异常 Object.defineProperty
      let then = x.then; // 取x的then方法 {then:{}}
      if (typeof then === 'function') { // 如果then是函数我就认为它是promise
        // call 第一个参数是this ,后面的是成功的回调和失败的回调
        then.call(x, y => { // 如果y是promise就继续递归解析promise
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, r => { // 只要失败了就失败了
          if (called) return;
          called = true;
          reject(r);
        });
      } else { // then是一个普通对象,就直接成功即可1
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else { // x = 123
    resolve(x); // x就是一个普通值
  }
}
class Promise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (data) => {
      if (this.status === 'pending') {
        this.value = data;
        this.status = 'resolved';
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulFilled, onRejected) {
    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
    let promise2;
    if (this.status === 'resolved') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onFulFilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
    if (this.status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
    if (this.status === 'pending') {
      promise2 = new Promise((resolve, reject) => {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulFilled(this.value);
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
        // 存放失败的回调
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      })
    }
    return promise2; // 调用then后返回一个新的promise
  }
}
module.exports = Promise;

测试

let Promise = require('./c.js');
let p = new Promise((resolve,reject)=>{
   resolve(123)
})
p.then( data => {
    console.log('qw',data)
},err => {
    console.log('p',err);
}).then((data)=>{
    console.log('sf',data);
},(err)=>{
    console.log('perr',err);
})

结果

测试结果

假设我们读取一个文件,代码如下:

let Promise = require('./a.js');
let fs = require('fs')
function read(){
    return new Promise((resolve,reject)=>{
        fs.readFile('./a.js','utf8',(err,data)=>{
            if(err) reject(err);
            resolve(data);
        })
    })
}

这样看还是有点繁琐,在promise里面有个语法糖

promise.deferred是一个语法糖,属于promise的一部分

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

为什么叫语法糖呢?有来这个属性之后我们可以把读文件的代码这样写

let Promise = require('./a.js');
let fs = require('fs')
function read(){
    let deffer = Promise.deffer();
        fs.readFile('./a.js','utf8',(err,data)=>{
            if(err) deffer.reject(err);
            deffer.resolve(data);
        })
    return deffer.promise;//保持他的then功能
}

可以帮我解决其中一层嵌套问题,但是对于错误处理不太方便了就。 这样我们创建一个基础的Promise,下面我们做一下

验证

需要npm install promise-aplus-tests -g 安装,然后在当前文件下npm promise-aplus-test 文件名 测试

当然Promise 也有很多扩展方法,像promise.all, catch(),promise.race(),promise.resolve(),promise.reject(),我们来看看他的这些扩展方法是如何实现的

  • catch() catch其实是then的简写,只是没有成功的回调,可以统一处理错误
then() =>{
    ...
}
catch(onrejected){
    return this.then(null,rejected)
}

测试

let Promise = require('./a.js');
let p = new Promise((resolve,reject)=>{
    reject('err');
})
p.then().then().catch(r=>{
    console.log(r)
}).then(data=>{console.log('data',data)})

[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
err
data undefined
  • promise.resolve() && promise.reject()
Promise.resolve = function (val) {
   return new Promise((resolve, reject) => {
        resolve(val);
    })
}
Promise.reject = function (val) {
    return new Promise((resolve, reject) => {
        reject(val);
     })
 }  

测试

let Promise = require('./a.js');
Promise.resolve("hello").then(err=>{
    console.log('data',err)
})

Promise.reject("qweerr").then().then(data=>{
    console.log('data',data
)},err =>{
    console.log('err',err)
})

[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
data hello
err qweerr
  • promise.all() && promise.race()
Promise.all = function (promises) {//数组里面是并发状态,有一个失败,就失败了
    return new Promise((resolve, reject) => {
        let i = 0;
        let arr = [];
        function processData(index,data){
            arr[index] = data;
            i++;
            if(i === promises.length){
                resolve(arr);
            }
        }
        for(let i=0;i<promises.length;i++){
            promises[i].then(data =>{
                processData(i,data)
            },reject)
        }
     })
 }  
 //和all对应的是race赛跑,,以最快的为结果
 Promise.race = function(promises){
     return new Promise((resolve,reject)=>{
        for(let i=0;i<promises.length;i++){
            promises[i].then(resolve,reject)
        }
     })
 }

测试

let Promise = require('./a.js');
let fs = require('fs');
function read(url){
   let defer = Promise.defer();
   fs.readFile(url,'utf8',(err,data)=>{
       if(err) defer.reject(err);
       defer.resolve(data);
   })
   return defer.promise;
}
Promise.all([
   read('./1.txt'),
   read('./2.txt')
]).then(arr=>{
   console.log('as' ,arr)
},err=>{
   console.log('sad',err)
})
Promise.race([
   read('./1.txt'),
   read('./2.txt')
]).then(arr=>{
   console.log('as' ,arr)
},err=>{
   console.log('sad',err)
})

[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
as [ 'hahahaahah', '\'我很帅\'' ]
as hahahaahah

bluebird && Q,支持语法糖Q库请参考

可以去git上查看 用法 命令行,在当前文件夹 npm install bluebird

let bluebird = require('bluebird')
let fs = require('fs')
//function read(url){
//    let defer = Promise.defer();
//    fs.readFile(url,'utf8',(err,data)=>{
//        if(err) defer.reject(err);
//        defer.resolve(data);
//    })
//    return defer.promise;
//}
//相比之前要创建的promise,使用bluebird可以帮我们把一步方法转换成promise方法
let read = bluebird.promisify(fs.readFile);
read('./2.txt','utf8').then(data=>{
   console.log(data)
})

[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
'我很帅'

那么他是如何实现的呢?

function promisify(fn){//promisify会给fn加上一个回调
    return function(...arg){
        return new Promise((resolve,reject)=>{
            fn(...arg,fnunction(err,data){
                if(err) resolve(err);
                reject(data);
            })
        })
    }
}
function promisifyAll(obj){//全部promise化
    for(let key in obj){
        if(typeof  obj[key] === 'function'){
            obj[key+'Async'] = promisify(obj[key])
        }
    }
}

基于promise 的generator和co的实现

generator 生成器 -> 生成的迭代器 ES6提供 let obj = {0:1,1:2,length:2,[symbol.iter]:function(){ return {}}}

  1. [symbol.iter]后面的函数叫迭代函数
  2. 迭代器函数会返回一个对象,
  3. 返回的对象中必须返回next方法,,然后返回value,done
  4. generator相当于把一个函数分成若干个部分执行,执行第一次时,将指针指向下一段代码,直到结束为止,,,
  5. 如果要在generator中调用另一个 gen() == {} 指向的是迭代器 ,需要用yield* gen()执行
let obj = {
   0:1,
   1:2,
   length:2,
   [symbol.iter]:
       function(){ 
           return {
               let index = 0;
               let that = this;
               return{
                   next(){
                       value:that[index],//值
                       done:index++ === that.length //done 代表是否迭代完成
                   }
               }
           }
      }
}
let arr = [...obj];
console.log(arr);

测试

function read(arr){
   let index = 0;
   return{
        next(){
            return {
               value:arr[index],
               done:++index === arr.length
            }
        }
    }
}
let it = read(['vue','react','node']);
console.log(it.next())
console.log(it.next())
console.log(it.next())

[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
{ value: 'vue', done: false }
{ value: 'react', done: false }
{ value: 'node', done: true }

我们同样可以用生成器函数实现

function * gen(){  //* 和 yield一起使用,产出
    yield 1000;
    yield 3000;
}
let a = gen();
console.log(a.next())
console.log(a.next())
[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
{ value: 1000, done: false }
{ value: 3000, done: false }

function * gen(){  //* 和 yield一起使用,产出
    return 1000;
}
let a = gen();
console.log(a.next())

[Running] node "/Users/myloveyunyun/Desktop/node/worker.js"
{ value: 1000, done: true }

  1. 这里我们要注意的是第一次next的参数没有任何意义,下一次next的参数是上一次yiled的返回值,我们这么写到底有什么用呢? 一般我们会和promise结合
let {promisify} = require('bluebird');
let fs = require('fs');
let read = promisify(fs.readFile);
let co = require('co');
function * gen(){
    let b = yield read('1.txt','utf8');
    let c = yield read('2.txt','utf8');
    return c
}
let it = gen();
it.next().value.then(data=>{
    it.next(data).value.then(data=>{
        console.log(it.next(data).value)
    })
})

我们可能会n多层嵌套,,有什么更好的方法,,Co 安装npm i co -g 他可以帮我们执行promise,我们可以讲带代码优化如下:

let co = require('co');
co(gen()).then(data=>{
    console.log(data)
})

co的实现

function co(it){
    //递归
    return new Promise((resolve,reject){
        function next(){//实现异步迭代
            let {value,done} = it.next(data);
            if(!done){
                value.then(data => {
                //当第一个执行完执行下一个then 
                    next(data)
                }) ,reject);//有一个失败就失败了
            }else{
                resolve(value)
            }
        }
    })
}

async + await 是generator的语法糖

  1. async返回的结果是promise
async function r(){
    let b = await read('a.txt','utf8');
    let c = await read('b.txt','utf8');
    return b;
}
r().then(data=>{})

原理

function _asyncToGenerator(fn){ // co(gen())
    return function(){
        var gen = fn.appaly(this,arguments); //gen() == it
        return new Promise(function(resolve,reject){
            function step(key,arg){
                try {
                    var info = gen[key](arg){
                    var value = info.value;
                }catch(error){
                    reject(error);
                    return
                }
                if(info.done){
                    resolve(vakue);
                }else{
                    return Promise.resolve(value).then(function(value){
                        step('next',value)//next
                    }),function(err){
                        step('throw',err)
                    })
                } 
            }
        })
    }
}

promise 可以支持多个并发请求,获取并发请求中的数据,我们要理解promise 本身是 同步的,但是他的then方法是异步的。 以上是我们对Promise的简单介绍,我们通过一个小例子来使我们的内容更加清晰

移动小球 一分钟学会所有异步实现

需求,id ball1,2,3 3个球第一个球移动后,下一个球移动

  1. 回调函数方法
function move(ele,position,cb){
    let left = 0;
    let timer = setInterval(() => {
        left += 5;
        if(left >= positon){
            clearaInterval(timer)
            ele.style.transform = `translate(${left}px)`
        }else{
            ele.style.transform = `translate(${left}px)`
        }
    },15)
}
move(ball1,500,function(){
    move(ball2,500,function(){
        move(ball3,500,function(){
            alergt('动完了')
        })
    })
})
  1. promise
function move(ele,position){
    return new Promise((resolve,reject) => {
        let left = 0;
        let timer = setInterval(() => {
            left += 5;
            if(left >= positon){
                clearaInterval(timer)
                ele.style.transform = `translate(${left}px)`;
                resolve();
            }else{
                ele.style.transform = `translate(${left}px)`
            }
        },15)
    })
}
move(ball1,500).then(data=>{return move(ball2,500)}).then(data=>{return move(ball3,500)}).then(alert('ok'))
  1. generator+co
    function * m(){
        yield move(ball1,500);
        yield move(ball2,500);
        yield move(ball3,500);
    }
    function co(it){
        return new Promise((resolve,reject)=>{
            function next(done,value){
                let {done,value} = it.next();
                if(done) return resolve(value)
                value.then(data=>{
                    next(data)
                },reject)
            }
            next();
        })
    }
    co(m()).then(data=>{
        alert('ok')
    })
  1. async+await
async function m(){
    await move(ball1,500);
    await move(ball2,500);
    await move(ball3,500);
}
m().then(data=>{
    alert('ok')
})