Javascript的优势之一是其如何处理异步代码。异步代码会被放入一个事件队列,等到所有其他代码执行后才进行,而不会阻塞线程

1 理解异步代码:

1.1 JavaScript最基础的异步函数是setTimeout和setInterval。setTimeout会在一定时间后执行给定的函数。它接受一个回调函数作为第一参数和一个毫秒时间作为第二参数。

console.log(1);

setTimeout(function() {
console.log('a');
},1000);
setTimeout(function() {
console.log('b');
},1000);
setTimeout(function() {
console.log('c');
},1000);
console.log(2);

正如预期,控制台先输出1、2,大约500毫秒后,再看到“a”、“b”、“c”。我用“大约”是因为setTimeout事实上是不可预知的。实际上,甚至 HTML5规范都提到了这个问题:

“这个API不能保证计时会如期准确地运行。由于CPU负载、其他任务等所导致的延迟是可以预料到的。”

1.2 Event Loop队列

有趣的是,直到在同一程序段中所有其余的代码执行结束后,超时才会发生。所以如果设置了超时,同时执行了需长时间运行的函数,那么在该函数执行完成之前,超时甚至都不会启动。实际上,异步函数,如setTimeout和setInterval,被压入了称之为Event Loop的队列。

Event Loop是一个回调函数队列。当异步函数执行时,回调函数会被压入这个队列。JavaScript引擎直到异步函数执行完成后,才会开始处理事件循环。这意味着JavaScript代码不是多线程的,即使表现的行为相似。事件循环是一个先进先出(FIFO)队列,这说明回调是按照它们被加入队列的顺序执行的。

2 JQuery异步处理

异步Javascript与XML(AJAX)永久性的改变了Javascript语言的状况。突然间,浏览器不再需要重新加载即可更新web页面。 在不同的浏览器中实现Ajax的代码可能漫长并且乏味. 但是有些地方还是需要注意

var data;
$.ajax({
url: "some/url/1",
success: function( data ) {
// 放在jquery指定的success函数里面可以保证异步请求完成
console.log( data );
}
})
// 这里并不能获取数据 ajax异步请求还未完成
console.log( data );

容易犯的错误,是在调用$.ajax之后马上使用data,但是实际上是这样的

xmlhttp.open( "GET", "some/ur/1", true );
xmlhttp.onreadystatechange = function( data ) {
if ( xmlhttp.readyState === 4 ) {
console.log( data );
}
};
xmlhttp.send( null ); 底层的XmlHttpRequest对象发起请求,设置回调函数用来处理XHR的readystatechnage事件。
然后执行XHR的send方法。在XHR运行中,当其属性readyState改变时readystatechange事件就会被触发,
只有在XHR从远端服务器接收响应结束时回调函数才会触发执行。

3 回调函数--处理异步

异步编程很容易陷入我们常说的“回调地狱”。因为事实上几乎JS中的所有异步函数都用到了回调,连续执行几个异步函数的结果就是层层嵌套的回调函数以及随之而来的复杂代码。

eg: Nodejs中常见异步函数

var fs = require( "fs" );
fs.exists( "index.js", function() { // 回调函数处理异步
fs.readFile( "index.js", "utf8", function( err, contents ) { // 回调函数处理异步
contents = someFunction( contents ); // do something with contents
fs.writeFile( "index.js", "utf8", function() { // 回调函数处理异步
console.log( "全部按顺序执行完" );
});
});
});
console.log( "executing..." );

3.1 清除嵌套回调

3.1.1 命名函数避免双层嵌套,解决嵌套回调

清除嵌套回调的一个便捷的解决方案是简单的避免双层以上的嵌套。传递一个命名函数给作为回调参数,而不是传递匿名函数:

4 事件--处理异步

事件是另一种当异步回调完成处理后的通讯方式。一个对象可以成为发射器并派发事件,而另外的对象则监听这些事件。这种类型的事件处理方式称之为 观察者模式

5 Promise(ES6)--处理异步

// 实例化 Promise对象
let time = function(time) {
return new Promise((resolve, reject) => {
console.log('Promise实例化完成'); // Promise实例化后立即执行
if (time >= 3000) {
setTimeout(function(){
resolve('大于3秒的时间后才显示出来');
}, time)
} else {
reject('时间不能小于3秒')
}
});
}; // 实例化Promise对象后才能使用 then
time(1000).then((value) => { // 1000 会走第二条判断Reject(Pending => Reject 失败会走第二个回调)
console.log(value);
}, (error) => {
console.log(error);
});
console.log(1); // 'Promise实例化完成' 1 '时间不能小于3秒'

6 ES7的Async/Await--处理异步

var sleep = function (time) {
return new Promise(function(resolve, reject) { // 返回一个promise对象
setTimeout(function() {
resolve();
}, time)
})
};
var start = async function() {
console.log('开始');
await sleep(3000); // 等待异步过程完成再往下执行
console.log('结束');
}
start(); // 控制台先输出start,稍等3秒后,输出了end。
基本规则
async 表示这是一个async(异步)函数,await只能用在这个函数里面。
await 表示在这里等待promise返回结果了,再继续执行。
await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了…)
获得返回值

await等待的虽然是promise对象,但不必写.then(..),直接可以得到返回值。

var sleep = function (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
// 返回 ‘ok’
resolve('ok');
}, time);
})
}; var start = async function () {
let result = await sleep(3000);
console.log(result); // 收到 ‘ok’
};
// 获取异步结果
let sleep = function (time) {
return new Promise(function(resolve, reject){
setTimeout(function() {
resolve('异步返回结果');
}, time)
});
}; let start = async function() {
let result = await sleep('3000');
console.log(result);
};
start(); // 3秒后输出 '异步返回结果'
// ** 捕获错误
var asyncFn = function (time) {
return new Promise((resolve, reject) => {
setTimeout(function() {
reject('异步出了问题');
}, time)
});
}; var start = async function() {
console.log('开始');
let result = await asyncFn(3000); // 返回了一个错误,不会往下执行了
console.log(result);
console.log('结束');
};
// 开始
// (node:2200) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): 异步出了问题
// (node:2200) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. // 既然.then(..)不用写了,那么.catch(..)也不用写,可以直接用标准的try catch语法捕捉错误。
var start = async function() {
try{
console.log('开始');
let result = await asyncFn(3000); // 返回了一个错误,不会往下执行了
console.log(result);
console.log('结束');
} catch(err) { //出了错误
console.log(err)
}
};
// 开始
// 异步出了问题 start();

参考资料

http://cnodejs.org/topic/5640b80d3a6aa72c5e0030b6

问题:

1 Promise是不是还是使用回调函数方式处理异步,但是避免了多重回调嵌套的问题?

  An: Promise是处理异步的一种方式和回调函数处理异步方式是平行关系

js-异步机制与同步机制的更多相关文章

  1. 【转】C#异步编程及其同步机制

    C#异步编程及其同步机制 本篇文章涵盖一下几部分内容: 1. 什么是异步编程,为什么会需要异步编程 2. .NET下的异步编程及其发展 3. .NET线程同步机制及线程间数据封送 4. 异步模式 5. ...

  2. 内核同步机制-RCU同步机制

    转自:https://blog.csdn.net/nevil/article/details/7718375 转自http://www.360doc.com/content/09/0805/00/36 ...

  3. Zookeeper的选举机制和同步机制超详细讲解,面试经常问到!

    前言 zookeeper相信大家都不陌生,很多分布式中间件都利用zk来提供分布式一致性协调的特性.dubbo官方推荐使用zk作为注册中心,zk也是hadoop和Hbase的重要组件.其他知名的开源中间 ...

  4. java 异步机制与同步机制的区别

    所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回.所以异步的同义语是非阻塞(None Blocking). 网上有很多网友用很通俗的比喻  把同步和异步讲解的很透彻 转过 ...

  5. nginx源代码分析--进程间通信机制 & 同步机制

    Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...

  6. Linux 多线程 - 线程异步与同步机制

    Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...

  7. Java中的闪光点:ThreadLocal是线程Thead的局部变量,可替代同步机制的设计,值得学习和研究

    线程局部变量ThreadLocal,是Java支持的一种线程安全机制,目的是解决多线程的并发问题. 具体来讲,就是多个线程访问该实例对象的变量时,该实例对象将其存储为键值对的形式,保证各个线程(键)分 ...

  8. linux kernel同步机制的思考

    在学习内核同步机制的时候,书中介绍了同步方法:原子操作(atomic).自旋锁(spinlock).信号量(semaphore).互斥锁(mutex).完成变量(completion).大内核(BLK ...

  9. js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)

    javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环 ...

随机推荐

  1. quartz源码分析——执行引擎和线程模型

    title: quartz源码分析--执行引擎和线程模型 date: 2017-09-09 23:14:48 categories: quartz tags: [quartz, 源码分析] --- - ...

  2. python 求解线性方程组

    Python线性方程组求解 求解线性方程组比较简单,只需要用到一个函数(scipy.linalg.solve)就可以了.比如我们要求以下方程的解,这是一个非齐次线性方程组: 3x_1 + x_2 - ...

  3. 学习Python Day1

    学习PythonDay1,主要是学习了循环,while循环,for循环:while循环加if.else以及elif,for循环嵌套for循环:break,终止循环,continue跳出循环:for i ...

  4. group by 详解

    1.概述 2.原始表 3.简单Group By 4.Group By 和 Order By 5.Group By中Select指定的字段限制 6.Group By All 7.Group By与聚合函 ...

  5. kickstart部署及使用

    Linux运维:kickstart : 矮哥linux运维群:93324526 1.环境检查 [root@m01 ~]# cat /etc/redhat-release CentOS release ...

  6. IPsec_VPN实现技术【转载】

    GRE Tunnel GRE Tunnel(General Routing Encapsulation 通用路由封装)是一种非常简单的VPN(Virtual Private Network 虚拟专用网 ...

  7. 为什么会需要消息队列(MQ)?

    为什么会需要消息队列(MQ)? #################################################################################### ...

  8. JS基础--函数与BOM、DOM操作、JS中的事件以及内置对象

       前   言 絮叨絮叨 这里是JS基础知识集中讲解的第三篇,也是最后一篇,三篇JS的基础,大多是知识的罗列,并没有涉及更难得东西,干货满满!看完这一篇后,相信许多正在像我一样正处于初级阶段的同学, ...

  9. 201521123008《Java程序设计》第六周实验总结

    1.本周学习总结 2.书面作业 1.clone方法 1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注意什么? 因为被protected修饰 ...

  10. 201521123053《Java程序设计》第四周总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 现在上课跟着老师的思路走,一般都能理解了.就是课上知识点有些难以记住. 特别讲讲这个思维导图 ...