深刻理解异步和同步以及异步编程有哪些方式

同步的概念:执行一个方法或者功能,在没得到结果前,其他方法不执行,一定得等当前方法执行完,才会执行下一步骤
异步的概念:执行一个方法或者功能,不需要等待到当前方法执行完,其他方法也可以执行
一. Javascript异步编程
Javascript是单线程的,因此异步编程对其尤为重要。
nodejs来说,外壳是一层js语言,这是用户操作的层面,在这个层次上它是单线程运行的,也就是说我们不能像Java、Python这类语言在语言级别使用多线程能力。取而代之的是,nodejs编程中大量使用了异步编程技术,这是为了高效使用硬件,同时也可以不造成同步阻塞。不过nodejs在底层实现其实还是用了多线程技术,只是这一层用户对用户来说是透明的,nodejs帮我们做了几乎全部的管理工作,我们不用担心锁或者其他多线程编程会遇到的问题,只管写我们的异步代码就好。
. Javascript异步编程的方法
在ES6之前,js主要的异步编程方式有3种:

(1) 回调函数
异步调用一般分为两个阶段,提交请求和处理结果,这两个阶段之间有事件循环的调用,它们属于两个不同的事件循环(tick),彼此没有关联。
异步调用一般以传入callback的方式来指定异步操作完成后要执行的动作。而异步调用本体和callback属于不同的事件循环。
一旦我们在异步调用函数中扔出一个异步I/O请求,异步调用函数立即返回,此时,这个异步调用函数和这个异步I/O请求没有任何关系。
//定义主函数,回调函数作为参数
function A(a,callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数B传进去
A(2,B);
//输出结果
我是主函数
我是回调函数
一下是nodejs 读取文件的列子:
//读文件后输出文件内容
var fs = require('fs');
fs.readFile('./text1.txt', 'utf8', function(err, data){
if (err){
throw err;
}
console.log(data);
});
假如读取多个文件嵌套式,回调少还好管理,多了就比较繁琐不容易维护,比较混乱,回调太多 ,这就是“回调地狱(callback hell)”
var fs = require('fs');
fs.readFile('./text1.txt', 'utf8', function(err, data){
fs.readFile('./text1.txt', 'utf8', function(err, data){
fs.readFile('./text1.txt', 'utf8', function(err, data){
});
});
});
由此,Promise的概念就由社区提出并实现,作用与回调方法几乎一致,都是在某种情况下执行预先设定好的方法,但是使用它却能够让代码变得更简洁清晰

(2)事件监听(事件的订阅和发布)
//jq 的注册侦听事件
$( ".ele" ).on( "click" , function (){ console .log( "clicking" );});
//触发事件$( ".ele" ).trigger( "click" );
//nodejs发布和订阅事件
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('event1', function(message){
console.log(message);
});
emitter.emit('event1', "message for you");

(3)ES6 Promise
在ES6发布了, Promise是异步编程的解决方案的一种
什么是Promise
Promise是异步编程的一种解决方案,它有三种状态,分别是pending-进行中、resolved-已完成、rejected-已失败
当Promise的状态又pending转变为resolved或rejected时,会执行相应的方法,并且状态一旦改变,就无法再次改变状态,这也是它名字promise-承诺的由来
promise对象:

//实例化的Promise对象会立即执行
let promise = new Promise ( (resolve, reject) => {
if ( success ) {
resolve(a) // pending ——> resolved 参数将传递给对应的回调方法
} else {
reject(err) // pending ——> rejectd
}
} )
Promise原型链上的then 方法
.then()方法是Promise原型链上的方法,它包含两个参数方法,分别是已成功resolved的回调和已失败rejected的回调
Promise原型链上的c atch 方法
Promise catch()的作用是捕获Promise的错误,与then()的rejected回调作用几乎一致。但是由于Promise的抛错具有冒泡性质,能够不断传递,这样就能够在下一个catch()中统一处理这些错误。同时catch()也能够捕获then()中抛出的错误,所以建议不要使用then()的rejected回调,而是统一使用catch()来处理错误

Promise的基本Api方法:
Promise.resolve()
用来包装一个现有对象,将其转变为Promise对象,但Promise.resolve()会根据参数情况返回不同的Promise:
参数是Promise:原样返回
参数带有then方法:转换为Promise后立即执行then方法
参数不带then方法、不是对象或没有参数:返回resolved状态的Promise
Promise.reject()
Promise.reject()会直接返回rejected状态的Promise
Promise.all() // 参数为Promise对象数组
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
p的状态由p1、p2、p3决定,分两种情况:
(1)只有p1、p2、p3的状态都变成 resolved ,p的状态才会变成 resolved ,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race() // 参数为Promise对象数组
参数中的p1、p2、p3只要有一个改变状态,promise就会立刻变成相同的状态并执行对于的回调
Promise.done()
Promise.done() 的用法类似 .then() ,可以提供resolved和rejected方法,也可以不提供任何参数,它的主要作用是在回调链的尾端捕捉前面没有被 .catch() 捕捉到的错误
Promise. finally()
接受一个方法作为参数,这个方法不管promise最终的状态是怎样,都一定会被执行
(4) Generator函数
Generator函数是协程在ES 6中的实现,最大特点就是可以交出函数的执行权(暂停执行)。
注意:在node中需要开启--harmony选项来启用Generator函数。
整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。
协程的运行方式如下:
第一步:协程A开始运行。
第二步:协程A执行到一半,暂停,执行权转移到协程B。
第三步:一段时间后,协程B交还执行权。
第四步:协程A恢复执行。
上面的协程A就是异步任务,因为分为两步执行。
function generator() { // ...其他代码 var f = yield readFile(fileA); // ...其他代码 }
generator 函数是一个协程,yield语句表示,执行到此处执行权就交给其他协程,也就是说,yield是两个阶段的分界线。协程遇到yield语句就暂停执行,直到执行权返回,再从暂停处继续执行。这种写法的优点是,可以把异步代码写得像同步一样
function* generator (x){
var y = yield x + 2;
return y;
}

var g = generator (1);
var r1 = g.next(); // { value: 3, done: false }
console.log(r1);
var r2 = g.next() // { value: undefined, done: true }
console.log(r2);
需要注意的是Generator函数的函数名前面有一个"*"。
上述代码中,调用Generator函数,会返回一个内部指针(即遍历器)g,这是Generator函数和一般函数不同的地方,调用它不会返回结果,而是一个指针对象。调用指针g的next方法,会移动内部指针,指向第一个遇到的yield语句,上例就是执行到x+2为止。
换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段。
Generator函数的数据交换和错误处理
next方法返回值的value属性,是Generator函数向外输出数据;next方法还可以接受参数,这是向Generator函数体内输入数据
function* generator (x){
var y = yield x + 2;
return y;
}

var g = generator (1);
var r1 = g.next(); // { value: 3, done: false }
console.log(r1);
var r2 = g.next(2) // { value: 2, done: true }
console.log(r2);
第一个next的value值,返回了表达式x+2的值(3),第二个next带有参数2,这个参数传入Generator函数,作为上个阶段异步任务的返回结果,被函数体内的变量y接收,因此这一阶段的value值就是2。
Generator函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
function* generator (x){
try {
var y = yield x + 2;
}
catch (e){
console.log(e);
}
return y;
}

var g = generator (1);
g.next();
g.throw('error!'); //error!
yield *语句
普通的yield语句后面跟一个异步操作,yield *语句后面需要跟一个遍历器,可以理解为yield *后面要跟另一个Generator函数,讲起来比较抽象,看一个实例。
//嵌套异步操作流
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

var generator = function* (){
var f1 = yield readFile('./text1.txt', 'utf8');
console.log(f1);

var f_ = yield * generator1 (); //此处插入了另外一个异步流程

var f2 = yield readFile('./text2.txt', 'utf8');
console.log(f2);

var f3 = yield readFile('./text3.txt', 'utf8');
console.log(f3);
};

var generator 1 = function* (){
var f4 = yield readFile('./text4.txt', 'utf8');
console.log(f4);
var f5 = yield readFile('./text5.txt', 'utf8');
console.log(f5);
}

function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen .next(data);
if (result.done) return;
result.value(next);
}

next();
}
run(gen); //自动执行

(5)ES 7中的async和await
async和await是ES 7中的新语法,但是可以通过Babel一类的预编译器处理成ES 5的代码。目前比较一致的看法是async和await是js对异步的终极解决方案。
async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行,async 函数返回的是一个promise 对象
async function testResult() {
let result = await doubleAfter2seconds(30);
console.log(result);
}
遇到await 之后,代码就暂停执行了 ,等待执行完 doubleAfter2seconds(30)
如果想尝个鲜,简单一点做法是执行:
cnpm install --global babel-cli
async_await.js代码如下:
async_await.js代码如下:
var fs = require('fs');
var readFile = function (fileName){
return new Promise(function (resolve, reject){
     fs.readFile(fileName, function(error, data){
     if (error){
reject(error);
}
else {
resolve(data);
}
});
});
};
var asyncReadFile = async function (){
    var f1 = await readFile('./text1.txt');
var f2 = await readFile('./text2.txt');
console.log(f1.toString());
console.log(f2.toString());
};
asyncReadFile();
接着执行 babel-node async_await.js
输出:
1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值