Promise

141 阅读4分钟

浏览器常驻的线程:

  1. js 引擎线程(解释和执行js 代码, JS内核-V8引擎 ,js引擎用来解释执行js代码 )
  2. GUI 线程(绘制用户界面, 与js 主线程是互斥的);
  3. http 网络请求线程(处理用户的get, post 等请求等, 返回结果后讲回调函数推入到任务队列);
  4. 定时器触发线程(setTimeout, setInterval 等待时间结束后把执行函数推入到任务队列中);
  5. 浏览器事件处理线程(将click, mouse 等交互事件发生后将这些事件放入到事件队列中);

Promise是什么,Promise是为了解决怎么样的问题,Promise最终的解决方案是什么

可直接进行迭代的数据类型: Array,arguments,NodeList, Map,Set,TypeArray,String

首先这些对象之所以可以直接进行迭代的原因是因为对象上具有Symbol([Symbol.iterator])属性,这个属性对应着一个生成器函数,函数执行会返回一个迭代器,迭代器对象里面有个next方法,对这个next方法进行执行会返回一个迭代状态相关的对象{value:xxx, done: false/true}

  var arr = [1, 2, 3, 4];
        console.log(arr[Symbol.iterator]());
        let iterator = arr[Symbol.iterator]();
        console.log(iterator.next());
        console.log(iterator.next());
        console.log(iterator.next());
        console.log(iterator.next());
        console.log(iterator.next());

生成器的实现

   function makeIterator() {
        let _this = this,
            index = 0;
        return {
          next() {
            if(index < _this.length){
              return {value: _this[index++], done: false}
            }

            return {value: undefined, done: true}
          }
        }
      }
      let arr = [1, 2, 3, 4]
      Array.prototype.makeIterator = makeIterator;
      let iter = arr.makeIterator();
      console.log(iter.next());
      console.log(iter.next());
      console.log(iter.next());
      console.log(iter.next());
      console.log(iter.next());

给对象配置Symbol.iterator接口

var obj = {
            a: 1,
            b: 2,
            c: 3,
            [Symbol.iterator]() {
              let index = 0;
              let map = new Map();
              let propNames = Object.getOwnPropertyNames(this);
              for(let i = 0; i < propNames.length; i ++){
                map.set(propNames[i],this[propNames[i]]);
              }
          
              return {
                next() {
                  var mapEntries = [...map.entries()];
                  if(index < map.size){
                    return {
                      value: mapEntries[index++][1],
                      done: false
                    }
                  }else{
                    return {value: undefined, done: true};
                  }
                }
              }
            }
          };

          for(let value of obj){
            console.log(value);
          }

迭代和遍历有什么区别

迭代从目标源,逐个抽取的方式来提取数据

目标源:

1.有序的

2.连续的

生成器函数

生成器函数中的return是迭代器判断是否迭代完成的标志,如果没有手动return,最后就会返回 {value: undefined, done: true},如果手动return,比如return 1,则会返回{value: 1, done: true},当然第一次判断迭代完成之后你在next还是会返回{value: undefined, done: true}

 function * test () {
          return 1;
        }

        let iter = test();
        console.log(iter);         // test {<suspended>}
        console.log(iter.next());  // {value: 1, done: true}

既然知道了生成器函数的写法,我们可以对之前部署的对象的Symbol.iterator接口进行改写。

var obj = {
            a: 1,
            b: 2,
            c: 3,
            [Symbol.iterator]: function * () {
              let index = 0;
              let map = new Map();
              let propNames = Object.getOwnPropertyNames(this);
              for(let i = 0; i < propNames.length; i ++){
                map.set(propNames[i],this[propNames[i]]);
              }
          
             
                  var mapEntries = [...map.entries()];
              while(index < mapEntries.length){
                yield mapEntries[index ++ ][1];
              }
                 
              
            }
          };

          for(let value of obj){
            console.log(value);
          }

async/await函数 = 生成器函数 + 执行器函数

为什么要有Promise

因为过多的嵌套回调函数,会造成回调地狱的问题,代码难以维护

let fs = require('fs');
fs.readFile("./name.txt", 'utf-8', function (err, data) {
  fs.readFile(data, 'utf-8', function (err, data) {
    fs.readFile(data, 'utf-8',function (err, data) {
      console.log(data);
    });
  })
})

异步?

首先js被设计为单线程的,这是为了防止DOM冲突的问题

事件轮询 异步任务会开启异步线程

异步的解决方案是事件轮询

事件轮询的核心是回调函数

回调函数 作为参数的函数就是回调函数,回调函数不一定是异步的

异步任务是否完成取决于promise对象的状态

promise三个状态:pendding(进行中),fullfilled(已经完成),rejected(失败)

promise写法
    let fs = require('fs');


    function readFile(pathName){
      return new Promise(function (res, rej) {
        fs.readFile(pathName, 'utf-8', function (err, data) {
          if(err) {
            rej(err);
          }
          res(data);
        })
      });
    }

    let p = readFile('./name.txt');

    p.then(function (data) {
      console.log(data);
    }, function (err) {
      console.log(err);
    });

promise对象的状态一旦改变,就不会再改变,而且同个对象通过继续调用then方法,只能够在then方法的对应的回调函数执行时取到promise对象的状态值,如果回调函数不匹配当前promise对象的状态,则取不到值


//promise变为成功的状态之后,调用成功的回调函数会取得到值
    let fs = require('fs');


    function readFile(pathName){
      return new Promise(function (res, rej) {
        fs.readFile(pathName, 'utf-8', function (err, data) {
          if(err) {
            rej(err);
          }
          res(data);
        })
      });
    }

    let p = readFile('./name.txt');

    p.then(function (data) {
      console.log('第一次调用' + data);
      p.then(function (data) {
        console.log('第二次调用' + data);
        p.then (function (data) {
          console.log('第三次调用' + data);
        });
      })
    });                //第一次调用./number.txt
                       //第二次调用./number.txt
                       //第三次调用./number.txt
//promise变为成功的状态之后,失败的回调函数不会执行,取不到值
let fs = require('fs');


function readFile(pathName){
  return new Promise(function (res, rej) {
    fs.readFile(pathName, 'utf-8', function (err, data) {
      if(err) {
        rej(err);
      }
      res(data);
    })
  });
}

let p = readFile('./name.txt');

p.then(function (data) {
  console.log('第一次调用' + data);
  p.then(function (data) {
    
  }, function (err) {
    console.log('第二次调用' + err);
    p.then (function (data) {
      console.log('第三次调用' + data);
    });
  })
});

//promise变为失败的状态之后,调用失败的回调函数会取得到值
let fs = require('fs');


function readFile(pathName){
  return new Promise(function (res, rej) {
    fs.readFile(pathName, 'utf-8', function (err, data) {
     
        rej(data);
      
    })
  });
}

let p = readFile('./name.txt');

p.then(function (data) {

}, function (err) {
  console.log('第一次调用 ' + err);
  p.then(function (data) {

  }, function (err) {
    console.log("第二次调用 " + err);
    p.then(function (data) {

    }, function (err) {
      console.log("第三次调用 " + err);     //第一次调用 ./number.txt
                                            //第二次调用 ./number.txt
                                            //第三次调用 ./number.txt
    });
  });
});

promise相关方法:Promise.resolve/Promise.reject(静态方法),promise.then / promise.catch Promise.resolve或者Promise.reject会返回一个具有resolve或者reject状态的promise对象

//调用Promise.resolve,然后调用对象的.then方法
let p1 = Promise.resolve(1);
p1.then( function (data) {
  console.log(data); //1
});

//调用Promise.reject,然后调用对象的.then方法
let p1 = Promise.reject(1);
p1.then( function (data) {
  console.log(data);
}, function (err) {
  console.log(err);  //1
});


//第二种方法,调用对象的.catch方法获取失败状态的promise对象的数据
let p1 = Promise.reject(1);
p1.catch(function (err) {
  console.log(err); //1
});

thenable对象,如果Promise.resolve()参数中传的不是原始值,而是一个对象,而且这个对象还有then方法,那么这个对象的then方法会被Promise.resolve()调用,并且then方法的两个参数会被注入promise改变状态相关的resolve/reject函数,如果调用的是Promise.reject的话,直接返回这个对象作为promise对象的状态对应值


//a是一个thenable对象
let a = {
  then (resolve, reject) {
    resolve(1);
  }
}

//被当做参数传入Promise.resolve会被调用then方法,而且把改变promise对象的resolve和reject函数注入then方法的参数之中,形参接收之后执行就能够改变p1的状态
let p1 = Promise.resolve(a);
p1.then(function (data) {
  console.log(data); // 1      这是成功的状态
});


//失败的状态
let a = {
  then (resolve, reject) {
    reject(1);  //调用了reject来改变promise对象的状态
  }
}
let p1 = Promise.resolve(a);
p1.catch(function (err) {
  console.log(err);
});


//thenable对象失效的情况
let a = {
  then (resolve, reject) {
    reject(1);
  }
}
let p1 = Promise.reject(a);
p1.catch(function (err) {
  console.log(err);    //只会返回原对象 $ node test
                        { then: [Function: then] }
});



promise一道简单的题

Promise.resolve().then(function (data) {
  console.log('promise1');
  setTimeout( () =>{
    console.log('setTimeout2');
  },0);
});

setTimeout( function (){ 
  console.log('setTimeout1');
  Promise.resolve().then(function (data) {
    console.log('promise2');
  });
},0);

//promise1
//setTimeout1
//promise2   
//setTimeout2

.then方法本身其实是同步的,.then方法会返回一个promise对象,如果promise对象还是pending状态时调用他的.then方法,会把回调函数先挂起,等到promise对象的状态改变时,回调函数会立马被推进微任务队列中,然后事件轮询机制会看微任务队列还有宏任务队列中的任务,优先执行微任务

let p1 = new Promise(function (res, rej) {
  res(1);
});
let p2;
let p3 = p1.then( function (data) {
  console.log(1);
  p2 = new Promise(function (res, rej) {
    setTimeout(function () {
      console.log(3);
      res(4);
    });
    setTimeout(function () {
      console.log(5);
    });
  });
  return p2;
}).then(function (data) {
  console.log(data);
});
setTimeout(function () {
  console.log(2);
},0);

// 1 2 3 4 5

promise的链式调用,promise.then仍然会返回一个promise对象这个对象内部的value值取决于promise.then中的回调函数的返回值, 如果返回值不是一个promise对象,那就会隐式调用Promise.resolve() 给这个promise.then返回的promise对象设定一个值,如果回调函数中本来返回的是一个promise对象,那promise.then返回的promise对象的值取决于这一个promise对象的值。

let p1 = Promise.resolve( new Promise(function (res, rej) {
  res(1);
})).then(function (data) {
  console.log(data);
});

Promise.all

Promise可以用来返回一个promise对象,这个对象的value值是多个异步请求的结果组成的数组,而且这个结果是多个异步请求都拿到结果之后才会返回,Promise.all接收的也是数组,但是这个数组特殊,数组的成员是promise对象

let p1 = new Promise(function (res, rej) {
          setTimeout( () => {
            res(1000);
          });
        }, 1000);
        let p2 = new Promise(function (res, rej) {
          setTimeout(() => {
            res(2000);
          }, 2000);
        });
        let p3 = new Promise( (res, rej) => {
          setTimeout( () => {
            res(3000);
          }, 3000);
        });

        let p4 = Promise.all([p1, p2, p3]);
        p4.then(function (data) {
          console.log(data);     //[1000, 2000, 3000]
        });

Promise.race

Promise.race返回的也是一个promise对象,Promise.race参数中的promise对象中哪个先变成pending,他的值就是Promise.race执行后返回的promise对象取到的值

let p1 = new Promise(function (res, rej) {
          setTimeout( () => {
            res(1000);
          });
        }, 1000);
        let p2 = new Promise(function (res, rej) {
          setTimeout(() => {
            res(2000);
          }, 2000);
        });
        let p3 = new Promise( (res, rej) => {
          setTimeout( () => {
            res(3000);
          }, 3000);
        });

        let p4 = Promise.race([p1, p2, p3]);
        p4.then(function (data) {
          console.log(data);     //1000
        });

使用promise读取文件

function readFile (fileName) {
  return new Promise(function (resolve,reject) {
    fs.readFile(fileName, 'utf-8', function (err, data) {
      resolve(data);
    });
  });
}

readFile('./name.txt')
.then(function (data) {
  console.log(data);
  return readFile(data)   //将name.txt的内容传入,即number.txt作为路径
})
.then(function (data) {
  console.log(data);
  return readFile(data) //将number.txt的内容传入,即score.txt作为路径
})
.then(function (data) {
  console.log(data); //将score.txt的值打印出来
})

async/await函数

//await是一个操作符 //等待一个Promise对象产出结果的操作手段 //功能是暂停async函数的执行,等待Promise处理后的结果 //假如Promise处理的结果是rejected,会抛出异常 async函数是通过一个隐式的Promise(pendding状态)