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. C#构造函数、操作符重载以及自定义类型转换

    构造器 构造器(构造函数)是将类型的实例初始化的特殊方法.构造器可分为实例构造器和类型构造器,本节将详细介绍有关内容. 实例构造器 顾名思义,实例构造器的作用就是对类型的实例进行初始化.如果类没有显示 ...

  2. h5drag事件

    在拖动目标上触发事件 (源元素):ondragstart - 用户开始拖动元素时触发ondrag - 元素正在拖动时触发ondragend - 用户完成元素拖动后触发释放目标时触发的事件:ondrag ...

  3. java动态代理(JDK和cglib实现对比)

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt214 JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特 ...

  4. HDU 6200 2017沈阳网络赛 树上区间更新,求和

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6200 题意:给个图,有2种操作,一种是加一条无向边,二是查询u,v之间必须有的边的条数,所谓必须有的边 ...

  5. 查看numpy.ndarray的数据类型

    使用ndarray数据时,如果希望知道数据的类型和维数,可以按照以下方法: Xxx.dtype  #xxx表示一个ndarray类型的变量,返回ndarray的数据类型 Xxx.shape  #xxx ...

  6. §--------算法分界线--------§

    如题 As said in the title~ 计算机的cpu计算从根源上由最基本的逻辑电路(晶体管)组成,由此衍生出最基本的数值运算:四则运算.而此后所有的高级算法都是建立在这个基本计算原理(逻辑 ...

  7. 【ctrl+A】与【ctrl+单击图层缩略图】有什么区别?

    如果这图层没有透明区域的话那和ctrl+A的效果是一样的! 但如果图层有不透明区域,那选中的就是图层中所有不透明的区域!

  8. 【Alpha】第四次Daily Scrum Meeting

    GIT 一.今日站立式会议照片 二.会议内容 1.采取老师提出的建议,考虑对送礼对象进行一个分类,这个在服务功能模块中完善. 2.回顾之前几次会议的内容,做一个小的总结,各抒己见,对每个人哪方面做得比 ...

  9. 团队作业8——第二次项目冲刺(Beta阶段)--第一天

    一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 学号 成员 贡献比 201421123001 廖婷婷 16% 201421123002 翁珊 15% 201421123004 ...

  10. 201521123110 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2. 书面作业 1.clone方法 1.1 Object对 ...