参考阮一峰的Generator 函数的含义与用法http://www.ruanyifeng.com/blog/2015/04/generator.html,有兴趣的朋友可以去看一下

什么是异步?

所谓”异步”,简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

  • 同步:假设你去了一家饭店,找个位置,叫来服务员,这个时候服务员对你说,对不起我是“同步”服务员,我要服务完这张桌子才能招呼你。那桌客人明明已经吃上了,你只是想要个菜单,这么小的动作,服务员却要你等到别人的一个大动作完成之后,才能再来招呼你,这个便是同步的问题:也就是“顺序交付的工作1234,必须按照1234的顺序完成”。

  • 异步:则是将耗时很长的A交付的工作交给系统之后,就去继续做B交付的工作,。等到系统完成了前面的工作之后,再通过回调或者事件,继续做A剩下的工作。
    AB工作的完成顺序,和交付他们的时间顺序无关,所以叫“异步”。

附带图片说明:

以下是读取文件事例说明(异步)

 

以下是读取文件事例说明(同步)

 

 

 

 

 

 

 

 

 

异步编程的方法大概有四种:

一、回调函数

二、事件监听

三、发布/订阅

四、promise对象

 

回调函数的概念

转载大神https://www.jianshu.com/p/40e459cfdc6f

一个函数被作为参数传递给另一个函数(在这里我们把另一个函数叫做“otherFunction”),回调函数在otherFunction中被调用。

/注意到click方法中是一个函数而不是一个变量
//它就是回调函数
//对象.属性(){}
$("#btn_1").click(function() {
    alert("Btn 1 Clicked");
});  
//以上相当于下面的说法:
function click() { // 它就是回调函数
    alert("Btn 1 Clicked");
}
$("#btn_1").click(click);  

回调函数是怎样运作的

因为函数在Javascript中是第一类对象,我们像对待对象一样对待函数,因此我们能像传递变量一样传递函数,在函数中返回函数,在其他函数中使用函数。当我们将一个回调函数作为参数传递给另一个函数是,我们仅仅传递了函数定义。我们并没有在参数中执行函数。我们并不传递像我们平时执行函数一样带有一对执行小括号()的函数。

需要注意的很重要的一点是回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”(就像它的名字一样)实现回调函数的基本原理
使用命名函数或者匿名函数作为回调

像之前的例子一样,第一种方法就是匿名函数作为回调(使用了参数位置定义的匿名函数作为回调函数)。第二种方式就是命名函数作为回调(定义一个命名函数并将函数名作为变量传递给函数)。

//第一种方法:匿名函数作为回调函数
var generalLastName = "Cliton";
function getInput(options, callback){
    var arr = [];
    arr.push(options);
    //将全局变量generalLastName传递给回调函数
    callback(generalLastName,arr);
}
getInput({name:"Rich",speciality:"Javascript"}, function(generalLastName,arr){
    console.log(generalLastName + ":" + arr[0].speciality) // Cliton:Javascript
});

//第二种方法:命名函数作为回调函数
var generalLastName = "Cliton";
function getInput(options, callback){
    var arr = [];
    arr.push(options);
    //将全局变量generalLastName传递给回调函数,就算里面形参名字一致也可以,使用全局变量
    callback(generalLastName,arr);
}
function call(generalLastName,arr){
    console.log(generalLastName + ":" + arr[0].speciality) // Cliton:Javascript
}
getInput({name:"Rich",speciality:"Javascript"}, call);
在执行之前确保回调函数是一个函数
function getInput(options, callback){
    //确保callback是一个函数   
    if(typeof callback === "function"){
        //调用它,既然我们已经确定了它是可调用的
        callback(options);
    }
}
回调地狱(回调十八层地狱)
var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
   p_client.open(function(err, p_client) {
       p_client.dropDatabase(function(err, done) {
           p_client.createCollection('test_custom_key', function(err, collection) {
               collection.insert({'a':1}, function(err, docs) {
                   collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                       cursor.toArray(function(err, items) {
                           test.assertEquals(1, items.length);
                           // Let's close the db
                           p_client.close();
                       });
                   });
               });
           });
       });
   });
解决方案:
1.给你的函数命名并传递它们的名字作为回调函数,而不是主函数的参数中定义匿名函数。
2.模块化L将你的代码分隔到模块中,这样你就可以到处一块代码来完成特定的工作。然后你可以在你的巨型应用中导入模块。

阮大神的解释我一个小白确实不是看得很懂,转载了一个大神的的文章,在这里补充说明一下阮大神里面的fs.readFile——这个是node.js里面的读取文件内容的回调函数语法,使用这个函数就可以读取文件内容。里面还有一个if()throw "500"主要是用来抛出错误的。

Promise

转载大神的promise说明和用法https://blog.csdn.net/qq_34645412/article/details/81170576#catch%E7%9A%84%E7%94%A8%E6%B3%95,大神这个博客说得很详细,看了基本都懂。

转载这个大神的简单版说明https://www.cnblogs.com/lunlunshiwo/p/8852984.html(如果不看上面大佬的解释,可能看不懂,反正我是这样)

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一。这句话说的很明白了,Promise是一种用于解决异步问题的思路、方案或者对象方式。在js中,经常使用异步的地方是Ajax交互。比如在es5时代,jQueryajax的使用success来完成异步的:

$.ajax({
   url:'/xxx',
   success:()=>{},
   error: ()=>{}
})

这种方法可以清楚的让读代码的人明白那一部分是Ajax请求成功的回调函数和失败的回调函数。但是问题来了,当一次请求需要连续请求多个接口时,这段代码仿佛进入了一团乱麻中

// 第一次 
$.ajax({
     url:'/xxx',
     success:()=>{
         // 第二次
         $.ajax({
             url:'/xxx',
             success:()=>{
               // 第三次
               $.ajax({
                  url:'/xxx',
                  success:()=>{
                   // 可能还会有
                  },
                  error: ()=>{}
                })
             },
             error: ()=>{}
        })
     },
     error: ()=>{}
})

也许因为success和error这两个函数的存在,理解这段代码会很简单,但是当我们更改需求的时候,这将成为一个棘手的问题。这就是回调地狱。

  当然,这是es5时代。当js这门语言发展到es6时代时,Promise的出现给异步带来了变革。Promise提供一个then,来为异步提供回调函数:

$.ajax({
    url:'/xxx',
}).then( ()=>{
   // 成功的回调
}, ()=>{
  // 失败的回调 
})

而其先进之处则是,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。

Promise的用法

  说完了Promise是什么,下面让我们研究一下Promise怎么使用。首先,Promise是一个对象,因此,我们使用new的方式新建一个。然后给它传一个函数作为参数,这个函数呢也有两个参数,一个叫resolve(决定),一个叫reject(拒绝),这两个参数也是函数。紧接着,我们使用then来调用这个Promise:

const fn = new Promise(function (resolve, reject) {
  setTimeout(()=>{
    let num = Math.ceil(Math.random() * 10) // 假设num为7
    if (num > 5) {
      resolve(num) //返回7
    } else {
      reject(num)
    }
  },2000)
})
fn.then((res)=>{
  console.log(res) // 7
},(err)=>{
  console.log(err)
})

      这就是最简单的Promise的使用。假设2秒钟之后生成随机数为7,因此resolve回调函数运行,then走第一个函数,console.log(7)。假设2秒钟之后生成随机数为3,因此reject回调函数运行,then走第二个函数,console.log(3)。

  那你可能说了,Promise要是就这点能耐也没什么大不了的啊?我们上面说了Promise的先进之处在于可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作:

fn = new Promise(function (resolve, reject) {
  let num = Math.ceil(Math.random() * 10)
  if (num > 5) {
    resolve(num)
  } else {
    reject(num)
  }
})
// 第一次回调
fn.then((res)=>{
  console.log(`res==>${res}`)
  return new Promise((resolve,reject)=>{
    if(2*res>15){
      resolve(2*res)
    }else{
      reject(2*res)
    }
  })
},(err)=>{
  console.log(`err==>${err}`)
}).then((res)=>{ // 第二次回调
  console.log(res)
},(err)=>{
  console.log(`err==>${err}`)
})

这就可以代替了上面类似es5时代的jQurey的success的嵌套式的回调地狱的产生,让代码清爽了许多。这里的resolve就相当于以前的success。

Promise的原理

  在Promise的内部,有一个状态管理器的存在,有三种状态:pending、fulfilled、rejected。

    (1) promise 对象初始化状态为 pending。

    (2) 当调用resolve(成功),会由pending => fulfilled。

    (3) 当调用reject(失败),会由pending => rejected。

  因此,看上面的的代码中的resolve(num)其实是将promise的状态由pending改为fulfilled,然后向then的成功回掉函数传值,reject反之。但是需要记住的是注意promsie状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变(记住,一定要记住,下面会考到)。

  当状态为fulfilled(rejected反之)时,then的成功回调函数会被调用,并接受上面传来的num,进而进行操作。promise.then方法每次调用,都返回一个新的promise对象 所以可以链式写法(无论resolve还是reject都是这样)。

Promise的几种方法

then

  then方法用于注册当状态变为fulfilled或者reject时的回调函数:

// onFulfilled 是用来接收promise成功的值
// onRejected 是用来接收promise失败的原因
promise.then(onFulfilled, onRejected);

        需要注意的地方是then方法是异步执行的。

// resolve(成功) onFulfilled会被调用
const promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // 状态由 pending => fulfilled
});
promise.then(result => { // onFulfilled
    console.log(result); // 'fulfilled' 
}, reason => { // onRejected 不会被调用
})

// reject(失败) onRejected会被调用
const promise = new Promise((resolve, reject) => {
   reject('rejected'); // 状态由 pending => rejected
});
promise.then(result => { // onFulfilled 不会被调用
}, reason => { // onRejected 
    console.log(rejected); // 'rejected'
})

catch

  catch在链式写法中可以捕获前面then中发送的异常。

fn = new Promise(function (resolve, reject) {
  let num = Math.ceil(Math.random() * 10)
  if (num > 5) {
    resolve(num)
  } else {
    reject(num)
  }
})
fn..then((res)=>{
  console.log(res)
}).catch((err)=>{
  console.log(`err==>${err}`)
})

其实,catch相当于then(null,onRejected),前者只是后者的语法糖而已。

resolve、reject

  Promise.resolve 返回一个fulfilled状态的promise对象,Promise.reject 返回一个rejected状态的promise对象。

Promise.resolve('hello').then(function(value){
    console.log(value);
});

Promise.resolve('hello');
// 相当于
const promise = new Promise(resolve => {
   resolve('hello');
});

// reject反之

all

  但从字面意思上理解,可能为一个状态全部怎么样的意思,让我看一下其用法,就可以看明白这个静态方法:

var   p1 = Promise.resolve(1),
      p2 = Promise.reject(2),
      p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then((res)=>{
    //then方法不会被执行
    console.log(results);
}).catch((err)=>{
    //catch方法将会被执行,输出结果为:2
    console.log(err);
});

大概就是作为参数的几个promise对象一旦有一个的状态为rejected,则all的返回值就是rejected。

  当这几个作为参数的函数的返回状态为fulfilled时,至于输出的时间就要看谁跑的慢了:

let p1 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('1s') //1s后输出
    resolve(1)
  },1000)
})
let p10 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('10s') //10s后输出
    resolve(10)
  },10000)
})
let p5 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('5s') //5s后输出
    resolve(5)
  },5000)
})
Promise.all([p1, p10, p5]).then((res)=>{
    console.log(res); // 最后输出
})

这段代码运行时,根据看谁跑的慢的原则,则会在10s之后输出[1,10,5]。over,all收工。

race

  promise.race()方法也可以处理一个promise实例数组但它和promise.all()不同,从字面意思上理解就是竞速,那么理解起来上就简单多了,也就是说在数组中的元素实例那个率先改变状态,就向下传递谁的状态和异步结果。但是,其余的还是会继续进行的。

let p1 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('1s') //1s后输出
    resolve(1)
  },1000)
})
let p10 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('10s') //10s后输出
    resolve(10) //不传递
  },10000)
})
let p5 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('5s') //5s后输出
    resolve(5) //不传递
  },5000)
})
Promise.race([p1, p10, p5]).then((res)=>{
    console.log(res); // 最后输出
})

因此,在这段代码的结尾我们的结果为

1s
1 //因为这个是速度最快的,rate的性质是只输出最快的,其他的就不输出了,至于为什么会打印是因为,已经new了一个对象出来,这个对象会自己执行,可是不会返回数据
5s
10s

我们可以根据race这个属性做超时的操作:

//请求某个图片资源
let requestImg = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
    });
//延时函数,用于给请求计时
let timeOut = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });

Promise.race([requestImg, timeout]).then((res)=>{
    console.log(res);
}).catch((err)=>{
    console.log(err);
});

重头戏!!!!实现一个简单的Promise

function Promise(fn){
  var status = 'pending'
  function successNotify(){
      status = 'fulfilled'//状态变为fulfilled
      toDoThen.apply(undefined, arguments)//执行回调
  }
  function failNotify(){
      status = 'rejected'//状态变为rejected
      toDoThen.apply(undefined, arguments)//执行回调
  }
  function toDoThen(){
      setTimeout(()=>{ // 保证回调是异步执行的
          if(status === 'fulfilled'){
              for(let i =0; i< successArray.length;i ++)    {
                  successArray[i].apply(undefined, arguments)//执行then里面的回掉函数
              }
          }else if(status === 'rejected'){
              for(let i =0; i< failArray.length;i ++)    {
                  failArray[i].apply(undefined, arguments)//执行then里面的回掉函数
              }
          }
      })
  }
  var successArray = []
  var failArray = []
  fn.call(undefined, successNotify, failNotify)
  return {
      then: function(successFn, failFn){
          successArray.push(successFn)
          failArray.push(failFn)
          return undefined // 此处应该返回一个Promise
      }
  }
}

下面附带正式使用promise的场景

// Promise 写法
  // 第一步:获取城市列表
  const cityList = new Promise((resolve, reject) => {
    $.ajax({
      url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city',
      success (res) {
        resolve(res)
      }
    })
  })

  // 第二步:找到城市是北京的id
    cityList.then(res => {
      let findCityId = res.filter(item => {
        if (item.id == 'c1') {
          return item
        }
      })[0].id
      
      findCompanyId().then(res => {
        // 第三步(2):根据北京的id -> 找到北京公司的id
        let findPostionId = res.filter(item => {
            if(item.cityId == findCityId) {
              return item
            }
        })[0].id

        // 第四步(2):传入公司的id
        companyInfo(findPostionId)

      })

    })

  // 第三步(1):根据北京的id -> 找到北京公司的id
  function findCompanyId () {
    let aaa = new Promise((resolve, reject) => {
      $.ajax({
        url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',
        success (res) {
          resolve(res)
        }
      })
    })
    return aaa
  }

// 第四步:根据上一个API的id(findPostionId)找到具体公司,然后返回公司详情
function companyInfo (id) {
  let companyList = new Promise((resolve, reject) => {
    $.ajax({
      url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',
      success (res) {
        let comInfo = res.filter(item => {
            if (id == item.id) {
               return item
            }
        })[0]
        console.log(comInfo)
      }
    })
  })
}

上面转载大神文章https://www.jianshu.com/p/1b63a13c2701

版权声明:本文为badaozongcai原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/badaozongcai/p/12809971.html