let img = new Image();
img.src = 'xxx';
img.onload = function(){
console.log('loaded')
};
console.log('init load')
//init load
//loaded
异步函数返回值的处理
异步函数的处理结果传递方式不能通过简单的函数返回值来进行传递,而需要通过回调函数参数来传递
function asyncAdd(a, callback) {
setTimeout(function () {
var result = a + 1;
callback(result);
}, 500);
}
var a = 1;
asyncAdd(a, function(result) {
console.log(result);
});
const co = require('co');
// errors can be try/catched
co(function *(){
try {
yield Promise.reject(new Error('boom'));
} catch (err) {
console.error(err.message); // "boom"
}
}).catch(onerror);
function onerror(err) {
console.error(err.stack);
}
co 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。co可以将一个generator函数处理成一个异步操作。这样你可以在generator函数里面使用yield来实现“顺序调用,异步执行”的效果。在co的4.0版本里它完全采用了Promise,它会将最终返回值作为参数传递到promise的then当中。
// 数组的写法
co(function* () {
var res = yield [
Promise.resolve(1),
Promise.resolve(2)
];
console.log(res);
}).catch(onerror);
// 对象的写法
co(function* () {
var res = yield {
1: Promise.resolve(1),
2: Promise.resolve(2),
};
console.log(res);
}).catch(onerror);
异步JavaScript
异步在javascript就是延时执行,异步函数没有返回值,值将会被传递给回调函数。也不能使用throw关键字
任务分成了两种:
异步任务通常可以分为两大类:I/O 函数(AJAX、readFile等)和计时函数(setTimeout、setInterval)
在js中,有许多场景是异步的:
1.定时器 setTimeout和setInterval 2.事件监听,如click,onload等事件 3.ajax请求
而在ES6诞生以前,js异步编程模型,大概有下面四种:
定时器并不会精准的执行,会存在一定的误差。定时器有诸多弊端,比如一道和setTimeout有关的经典笔试题:
因为异步函数必须等主进程运行完毕才会运行,setTimeout()内部回调运行的时候,主进程已经运行完毕了,此时i=10,所以输出10。
while 循环阻塞 setTimeout 执行
上面代码,我们期望 console 在 1s 后打出结果,可事实却是在 2000ms+ 之后运行的,这就是 Javascript 单线程给我们带来的烦恼,while 循环阻塞了 setTimeout 的执行。
try...catch捕获不到错误
异步函数的处理结果传递方式不能通过简单的函数返回值来进行传递,而需要通过回调函数参数来传递
加了 defer 之后
<script>
放在<head>
或<body>
是没有区别的。 async - 脚本的并行化这两个脚本会以任意次序运行,而且会立即运行,不论文档是否就绪。
如果同时使用 defer 和 async ,async 会覆盖掉 defer 。
JavaScript 语言对异步编程的实现,就是回调函数,但并不是回调函数就是异步。回调函数本身并没有问题,它的问题出现在多个回调函数嵌套。假如读取A文件之后,再读取B文件,循环下去,就会出现回调地狱(callback hell)。Promise 对象就是为解决这个问题而提出的
它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。采用 Promise,连续读取多个文件,写法如下:
Node与异步非阻塞I/O
我们知道,Node是基于事件驱动的单线程异步非阻塞模型, node是单线程的,异步是通过一次次的循环事件队列来实现的。每当接收到一个请求的时候,node的javascript模块会调用底层的c++模块,对传入的回调事件进行封装成请求对象,然后放入I/O线程池等待,由观察者管控何时调用。然后Javascript线程继续执行后续的操作。
所以,在Node中,除了代码,一切都是并行的:
以上代码异步读取file内容,后面代码不受I/O阻塞,读取完成后执行后面的回调函数
es6与异步操作
async函数可以看做多个异步操作包装成的一个Promise对象,而await命令就是内部then命令的语法糖。async函数返回Promise对象
await关键字只能用在aync定义的函数内,如果把上述的for循环改为forEach则会出错,因为此时await不在async函数的上下文。
await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。也可以是原始类型的值(但这时等同于同步操作)
详情见:Promise 探索与实践
Promise.race(iterable)
方法返回一个 promise,在可迭代的 resolves 或 rejects 中 promises 有一个完成或失败,将显示其值或原因。co函数库与并发的异步操作
co函数接受 Generator 函数作为参数,返回Promise对象,使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。
co 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。co可以将一个generator函数处理成一个异步操作。这样你可以在generator函数里面使用yield来实现“顺序调用,异步执行”的效果。在co的4.0版本里它完全采用了Promise,它会将最终返回值作为参数传递到promise的then当中。
总结
回调函数: 优点:简单、容易理解 缺点:不利于维护,代码耦合高
事件监听(采用时间驱动模式,取决于某个事件是否发生) 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数 缺点:事件驱动型,流程不够清晰
Promise对象 优点:可以利用then方法,进行链式写法;可以书写错误时的回调函数; 缺点:编写和理解,相对比较难
Generator函数 优点:函数体内外的数据交换、错误处理机制 缺点:流程管理不方便
async函数 优点:内置执行器、更好的语义、更广的适用性、返回的是Promise、结构清晰。 缺点:错误处理机制