异步编程的几种形式:
回调函数形式:
- function f1(callback){
- callback();
- }
- function f2(callback){
- callback();
- }
- function f3(callback){
- callback();
- }
- f1(f2(f3))
- 复制代码
这种方式实现异步编程优点是思路清晰,以串行的思考方式进行编程,缺点是形成回调地狱,过多的回调嵌套使得代码变得难以理解拆分和维护。
发布订阅模式
- let dep = {
- list: [],
- on: function (fn) {
- list.push(fn);
- },
- emit: function () {
- this.list.forEach(event => {
- typeof event === 'function' ? event() : null;
- })
- }
- };
- 复制代码
上面就是简易版的发布订阅模式:发布者存在一个数组list用于登记订阅者即异步执行的函数,等到一定条件下执行emit,订阅的异步函数都会执行。这就好比发布者售楼中心的拥有一个登记册,里面登记需要买房的所有订阅者,有的订阅者登记的是电话通知,有的订阅者登记的邮件通知,等到楼盘信息变化时会主动给每个订阅者执行相应的操作(执行对应的函数)
promise
promise的核心原理其实就是发布订阅模式,通过两个队列来缓存成功的回调(onResolve)和失败的回调(onReject)。如果还不熟悉promise用法的朋友,请参考Es6入门之promise对象,下面来分析promise的特点。
promise的特点:
- new Promise时需要传递一个executor执行器,执行器会立刻执行
- 执行器中传递了两个参数:resolve成功的函数、reject失败的函数,他们调用时可以接受任何值的参数value
- promise状态只能从pending态转onfulfilled,onrejected到resolved或者rejected,然后执行相应缓存队列中的任务
- promise实例,每个实例都有一个then方法,这个方法传递两个参数,一个是成功回调onfulfilled,另一个是失败回调onrejected
- promise实例调用then时,如果状态resolved,会让onfulfilled执行并且把成功的内容当作参数传递到函数中
- promise中可以同一个实例then多次,如果状态是pengding 需要将函数存放起来 等待状态确定后 在依次将对应的函数执行 (发布订阅)
promise基础版实现
下面针对这些特点来实现promise:
new Promise时需要传递一个executor执行器,执行器会立刻执行;执行器中传递了两个参数:resolve成功的函数、reject失败的函数,他们调用时可以接受任何值的参数value
- function Promise (executor){
- function resolve(value){}
- function reject(value){}
- try{
- executor(resolve,reject);
- }catch(e){
- reject(e);
- }
- }
- var promise = new Promise((resolve,reject)=>{
- console.log('start');
- })
- 复制代码
promise状态只能从pending态转onfulfilled,onrejected到resolved或者rejected
- function Promise (executor) {
- var self = this;//resolve和reject中的this指向不是promise实例,需要用self缓存
- self.state = 'padding';
- self.value = '';//缓存成功回调onfulfilled的参数
- self.reson = '';//缓存失败回调onrejected的参数
- self.onResolved = []; // 专门存放成功的回调onfulfilled的集合
- self.onRejected = []; // 专门存放失败的回调onrejected的集合
- function resolve (value) {
- if(self.state==='padding'){
- self.state==='resolved';
- self.value=value;
- self.onResolved.forEach(fn=>fn())
- }
- }
- function reject (reason) {
- self.state = 'rejected';
- self.value = reason;
- self.onRejected.forEach(fn=>fn())
- }
- try{
- executor(resolve,reject)
- }catch(e){
- reject(e)
- }
- }
- 复制代码
promise实例,每个实例都有一个then方法,这个方法传递两个参数,一个是成功回调onfulfilled,另一个是失败回调onrejected;
promise实例调用then时,如果状态resolved,会让onfulfilled执行并且把成功的内容当作参数传递到函数中;
promise中可以同一个实例then多次,如果状态是pengding 需要将函数存放起来 等待状态确定后 在依次将对应的函数执行(发布订阅)
- Promise.prototype.then=function (onfulfilled,onrejected) {
- var self=this;
- if(this.state==='resolved'){
- onfulfilled(self.value)
- }
- if(this.state==='rejected'){
- onrejected(self.value)
- }
- if(this.state==='padding'){
- this.onResolved.push(function () {
- onfulfilled(self.value)
- })
- }
- }
- 复制代码
then方法的特点:
以上只是实现了promise的基本功能,但是还缺少then的链式调用,then函数参数onfulfilled,onrejected缺省处理,链式调用返回值多种情况的处理,下面分析then方法的特点:
- 因为promise状态确定后就是不能更改,所以每次promise执行then后都会返回一个新的promise而不是this,那么状态永远为resolve或jeject,将存在异步调用
- onfulfilled或onrejected是一个可选参数,需要做没有传递时的处理
- 如果then中onfulfilled或onrejected返回的是一个普通值的话会把这个结果传递下一次then中的成功回调
- 如果then中onfulfilled或onrejected出现异常,会走下一个then的失败回调,将err传递到失败回调中
- 如果失败后还可以成功,如果返回undefined,会把undefined传递给下一次
- 如果then方法返回的是一个promise,那么会等待这个promise执行完决定返回的是成功还是失败
promise优化版实现
因为promise状态确定后就是不能更改,所以每次promise执行then后都会返回一个新的promise而不是this,那么状态永远为resolve或jeject,将存在异步调用;onfulfilled或onrejected是一个可选参数,需要做没有传递时的处理
- Promise.prototype.then=function (onfulfilled,onrejected) {
- onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;//onfulfilled缺省处理
- onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};//onrejected缺省处理
- var self=this,promise2=new Promise(function(resolve,reject){//返回一个promise
- if(this.state==='resolved'){
- try{
- onfulfilled(self.value);//里面会执行resolve
- }catch(e){
- reject(e);
- }
- }
- if(this.state==='rejected'){
- try{
- onrejected(self.value);
- }catch(e){
- reject(e);
- }
- }
- if(this.state==='padding'){//将执行过程缓存
- self.onResolved.push(function () {
- try{
- onfulfilled(self.value);
- }catch(e){
- reject(e)
- }
- });
- self.onRejected.push(function () {
- try{
- onrejected(self.value);
- }catch(e){
- reject(e)
- }
- })
- }
- })
- return promise2;
- }
- 复制代码
如果then中onfulfilled或onrejected返回的是一个普通值的话会把这个结果传递下一次then中的成功回调;
如果then中onfulfilled或onrejected出现异常,会走下一个then的失败回调,将err传递到失败回调中;
如果失败后还可以成功,如果返回undefined,会把undefined传递给下一次;
如果then方法返回的是一个promise,那么会等待这个promise执行完决定返回的是成功还是失败,所以需要对onfulfilled或onrejected的返回值进行处理。
- Promise.prototype.then=function (onfulfilled,onrejected) {
- onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
- onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
- var self=this,
- res=null,//用来缓存onfulfilled或onrejected的返回值
- promise2=new Promise(function(resolve,reject){
- if(this.state==='resolved'){
- try{
- res = onfulfilled(self.value);//得到onfulfilled的返回值
- resolvePromise(promise2,res,resolve,reject);//返回值的处理函数
- }catch(e){
- reject(e);
- }
- }
- if(this.state==='rejected'){
- try{
- res = onrejected(self.value);//得到onrejected的返回值
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e);
- }
- }
- if(this.state==='padding'){
- self.onResolved.push(function () {
- try{
- res = onfulfilled(self.value);
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e)
- }
- });
- self.onRejected.push(function () {
- try{
- res = onrejected(self.value);
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e)
- }
- })
- }
- })
- return promise2;
- }
- function resolvePromise(promise,res,resolve,reject) {
- if(promise===res){//防止循环引用
- return reject(new TypeError('循环引用'))
- }
- let called;//防止重复执行
- if(res!==null&&(typeof res==='function'||typeof res ==='object')){
- try {//防止promise执行报错
- let then=res.then;//判断是否promise就判断是否存在then方法
- if(typeof then ==='function'){//如果返回的是promise,只需要在返回的promise的then方法中下一步需要执行的函数
- then.call(res,(res2)=>{
- if (called) return;
- called = true;
- resolvePromise(promise,res2,resolve,reject);//如果是promise继续递归执行,直到不是promise,依次执行外层的resolve,让promise状态改变
- },)
- }else{//如果不是promise,有可能是undefine、onfulfilled或onrejected的返回的普通值,就直接将这个值返回,将外层的promise状态改变
- if (called) return;
- called = true;
- resolve(then)
- }
- }catch(e){
- if (called) return;
- called = true;
- reject(e)
- }
- }else{
- resolve(res)
- }
- };
- 复制代码
promise.then属于异步微任务,then中的方法,必须等到所有的同步任务执行完才执行,宏任务和微任务可以查看EventLoop其实如此简单
- console.log(1);
- var promise=new Promise(function(resolve,reject){
- resolve('a');
- })
- promise.then(function(value){
- console.log(value)
- })
- console.log(2);
- // 1 2 'a'
- 复制代码
- //由于promise有内部的机制实现微任务,所以这里使用setTimeout代替
- Promise.prototype.then=function (onfulfilled,onrejected) {
- onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
- onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
- var self=this,
- res=null,
- promise2=new Promise(function(resolve,reject){
- if(this.state==='resolved'){
- setTimeout(()=>{
- try{
- res = onfulfilled(self.value);
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e);
- }
- })
- }
- if(this.state==='rejected'){
- setTimeout(()=>{
- try{
- res = onrejected(self.value);
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e);
- }
- })
- }
- if(this.state==='padding'){
- self.onResolved.push(function () {
- setTimeout(()=>{
- try{
- res = onfulfilled(self.value);
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e);
- }
- })
- });
- self.onRejected.push(function () {
- setTimeout(()=>{
- try{
- res = onrejected(self.value);
- resolvePromise(promise2,res,resolve,reject);
- }catch(e){
- reject(e);
- }
- })
- })
- }
- })
- return promise2;
- }
- 复制代码
promise.catch会捕获到没有捕获的异常;
Promise.resolve会返回一个状态位rsolved的promise;
Promise.reject会返回一个状态位rsolved的promise;
Promise.all会在所有的promise resolved后执行回调,Promise.race只要有一个promise resolved就执行回调。
promise.catch将所有的错误在promise实例的下一个then中返回
- Promise.prototype.catch = function (onrejected) {
- return this.then(null, onrejected)
- };
- 复制代码
Promise.reject和Promise.reject
- Promise.reject = function (reason) {
- return new Promise((resolve, reject) => {
- reject(reason)
- })
- };
- Promise.resolve = function (value) {
- return new Promise((resolve, reject) => {
- resolve(value);
- })
- };
- 复制代码
Promise.all和Promise.race
- //在每个promise的回调中添加一个判断函数processData(就是在当前的promise.then中添加),每个promise状态改变后让index++,直到和promises的个数相等就执行回调
- Promise.all=function (promises) {
- return new Promise((resolve,reject)=>{
- let results=[],i=0;
- for(let i=0;i<promises.length;i++){
- let p=promises[i];
- p.then((data)=>{
- processData(i,data)
- },reject)
- }
- function processData (index,data) {
- results[index]=data;
- if(++i==promises.length){
- resolve(results)
- }
- }
- })
- };
- //在每个promise的回调中添加一个resolve(就是在当前的promise.then中添加),有一个状态改变,就让race的状态改变
- Promise.race=function (promises) {
- return new promises((resolve,reject)=>{
- for(let i=0;i<promises.length;i++){
- let p=promises[i];
- p.then(resolve,reject)
- }
- })
- };
- 复制代码
generator + co
Generator函数可以通过yield暂停执行和next恢复执行,所以可以封装一个函数来自动执行next函数而使Generator完成异步任务。
- let fs = require('mz/fs');
- function * read() {
- let age = yield fs.readFile('./name.txt','utf8');
- let adress = yield fs.readFile(age,'utf8');
- let r = yield fs.readFile(adress,'utf8');
- return r;
- }
- function co(it) {//it为一个generator对象
- //返回一个promise并且执行generator对象的next
- return new Promise((resolve,reject)=>{
- function next(data) {
- //获取前一个异步函数的返回值,其必须为promise
- let { value, done } = it.next(data);
- if(!done){
- //返回promise的then方法中添加generator的next
- //前一个异步函数执行成功会使返回的promise成resolved
- //然后执行generator的next,递归依次类推
- value.then(data=>{
- next(data)
- }, reject);
- }else{
- resolve(value);
- }
- }
- next();
- })
- }
- co(read()).then(data=>{
- console.log(data);
- },err=>{
- console.log(err);
- });
- 复制代码
async和awit
async+awit等于generator+co,而co中实现generator自动化是基于Promise,所以awit会使用new Promise形式。
结语
如果以上有任何错误之处,希望提出并请指正,如果对promise使用还不清楚的朋友,请参考Es6入门之promise对象,本文参考: