站长杂谈 > web前端
web前端web前端

异步与同步

1.1    基本概念
        同步:一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
 
 
异步:每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
 
案例分析:
悟空去火车票代售窗口买票,现在该悟空购票了,悟空后面是八戒,他也等着买票。
 
1、 
悟空:买一张明天到大理的火车票
售票MM:对不起,明天到大理的火车票已经没有了
悟空:啊!居然没有了,你等我一下,我打电话问问我女朋友,改买多久的票。 (此时,售票员什么事情都做不了,只得等我打完电话,而我背后的八戒变得非常的焦躁,心中:“我靠,耽误我时间,浪费我青春,你难道就不知道去一边打电话吗?等我买了票,你差不多也打完了。”)
30秒过去了.....
悟空:美女,那就换一张后天到大理的火车票吧。
售票MM:好的,给你。
八戒:买一张去源码时代的火车票,我要去学H5.
....
....
 
2、
悟空:买一张明天到大理的火车票
售票MM:对不起,明天到大理的火车票已经没有了
悟空:好吧,那我先打电话问问我女朋友,问好后,我在跟你说。(一个小的举动,从售票窗口让开) 。
八戒:买一张去源码时代的火车票,我要去学H5.
售票MM:好的,给你。
.....
.....
悟空电话打完了
悟空:美女,那就换一张后天到大理的火车票吧。
售票MM:好的,给你。
 
1、同步:总是等待上一个任务完成后,才开始下一个任务的执行。(注意:每个任务有执行时间)。

2、异步:开始一个(异步)任务,任务去执行,但是不会让后面的任务等待(阻塞),后面的任务继续执行,(异步)任务执行完毕后,会进行通知(回调)。

1.2    异步编程实现
NodeJS中共有三种编程方式:
1、回调函数
2、事件(基于回调)
3、Promise(ES6)
1.2.1 回调函数
Javascript语言的执行环境是"单线程"(single thread)。
所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

 
这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
 
 
"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
 
"异步模式"非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。
 
setTimeout(callback,ms); //延迟ms毫秒后,执行callback函数,延迟的过程,不会影响后面代码的执行。
 
setTimeout(function(){
  console.log(“1”);
},1000);
console.log(“2”);
 
var s = 13;
setTimeout(function(){
   s = 31;
},1000);
console.log(s);
 
回调函数本质还是一个函数,只是调用的时机是等待某个任务完成之后调用。
 
例如:
 
先定义了两个函数:   
 
 
function f2(){
    alert(“我是f2任务”);
}
 
 
function f1(callback){
    setTimeout(function () {
      // f1的任务代码,此处可以又很多的代码,消耗很长时间。
      callback();
    }, 5000);
  }
 
 
调用:
 
f1(f2); //此处的f2就是回调函数.f2作为一个参数传递进来的。
 
 
1.2.2 事件
//fs:文件系统模块,提供对文件的操作
 var fs = require("fs"); //创建输出流,读取文件
 var stream = fs.createReadStream("./NodeJs-DAY01.docx"); //读取数据事件,每次读取都会触发。
 stream.on("data",function(data){     console.log(data); }); //读取数据完毕
 stream.on("end",function(){     console.log("文件读取完毕"); });
1.2.3 Promise
 
先看一个例子:
深度嵌套
 
很多刚开始写nodejs代码的人,由于思路还停留在同步的思维,所以或多或少写过这样的代码:
 
func1(err, function(err1, data1) {
  func2(err1, function(err2, data2) {
    func3(err3, function(err3, data3) {
      func4(err4, function(err4, data4) {
        .......
      });
    });
  });
});
先别说这样的代码是否易于维护,光样子就够难看,代码都“斜”了。
 
本质:异步通知位置在回调函数里面,导致了这种多层嵌套。
如果使用Promise,就把通知位置放到then函数里面了,就形成一根线,而不是无限嵌套的回调函数里面。
 
 
(题外话:Less Callback, More Girls)
 
 
Promise ?prɑ:m?s  (承诺),就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
 
Promise是一种规范,一种解决callcack深层嵌套的方案,一种带有then方法支持链式操作的框架.
 
Promise 对象有以下两个特点:
(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
 
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
 
 
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。
如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。
 
var promise = new Promise(function(resolve, reject) {
 
 //请求加载一个文件
 //
 if (/* 异步操作成功 */){
        resolve(“”);//改变promise 对象的状态为成功
 } else {
        reject(“”);//改变promise 对象的状态为失败
 }
});
 
promise.then(function(value) {
 // success
}, function(value) {
 // failure
});
 
 
 
举例:
 
//promise(承诺)对象:异步操作编程,该对象描述整个异步操作过程。 进行中--->已完成 或 进行中--->已失败
//Promise三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。
//Pending 进行> Resolved完成
//Pending 进行> Rejected失败
//状态不可逆。


function test(){
    //创建一个Promise对象。对象帮我们完成异步的逻辑。
   
var promise = new Promise(function(resolve,reject){
        //传入的这个函数立刻执行。//该函数中编写异步代码

        //resolve,reject都是函数
        //resolve : 当异步任务完成的时候就请调用“resolve()”
        //reject : 当异步任务失败的时候就请调用“reject()”

       
var time = setTimeout(function(){
            console.log("开始执行1");
            resolve("hehe");
        },3000);
    });
    return promise;//承诺对象返回
}

function test2(){
    //创建一个Promise对象。对象帮我们完成异步的逻辑。
   
var promise = new Promise(function(resolve,reject){
        //传入的这个函数立刻执行。//该函数中编写异步代码

        //resolve,reject都是函数
        //resolve : 当异步任务完成的时候就请调用“resolve()”
        //reject : 当异步任务失败的时候就请调用“reject()”

       
var time = setTimeout(function(){
            console.log("开始执行2");
            resolve("hehe");
        },1000);
    });
    return promise;//承诺对象返回
}

function test3(){
    //创建一个Promise对象。对象帮我们完成异步的逻辑。
   
var promise = new Promise(function(resolve,reject){
        //传入的这个函数立刻执行。//该函数中编写异步代码

        //resolve,reject都是函数
        //resolve : 当异步任务完成的时候就请调用“resolve()”
        //reject : 当异步任务失败的时候就请调用“reject()”

       
var time = setTimeout(function(){
            console.log("开始执行3");
            resolve("hehe");
        },1000);
    });
    return promise;//承诺对象返回
}

var p = test();
//承诺对象的方法
/**
 * then(resolvedCb,rejectCb);该方法在已完成或失败后执行。
 *  该方法接收两个参数
 resolvedCb:已完成后将执行的回调;
 rejectCb:失败后将执行的回调;
 *
 */

p.then(function(){
    //已完成
   
console.log("任务完成1");
    return test2();
}).then(function(){
    console.log("任务完成2");
    return test3();
}).then(function(){
    console.log("任务完成3");
});



 
 
总结:
 
1、  需要定义一个函数,把任务放进函数里面。
2、  函数里面需要实例化一个Promise对象,并且把这个对象作为函数的返回值。
3、  在特定的时候,改变Promise对象的执行状态。
4、在then里面调用下一个包含Promise对象的任务函数。并且最终返回一个Promise对象。
 

web前端ruitiancnweb前端z_vae@sina.com

  •      网页设计中常听的属性名:内容(content)、内边距(padding)、边框(b...
    2018-07-17 22:09
  •      1 1 console概述console对象代表控制台,与Chrome中的控制台一样...
    2018-07-31 14:16
  •      1 两个函数是否等价function fun1() { return { ...
    2018-10-15 17:44
web前端