本文思路
- 什么是异步
- ES5异步实现方案
- ES6 promise解决了什么问题
- promise应该如何使用
1.异步
JavaScript同步模式有如下特点:
- 1个:解释执行JavaScript代码的线程
- 顺序:代码必须从上到下依次执行
- 缺点:某代码片段执行很久,造成阻塞。
JavaScript异步模式: - 新增任务队列
主线程仍由上到下执行,需要异步操作,则将操作新增到任务队列,主线程优先执行,然后在是任务队列,任务队列中遵守:消息队列,事件循环
保证了主要代码的迅速执行,异步操作在另外的地方去依次排队 - 回调函数
主线程需要发起异步,发起异步的函数一般为注册函数,其内部包含异步操作封装为callback(回调函数) - 现实例子
在公路上,汽车一辆接一辆,有条不紊的运行。这时,有一辆车坏掉了。假如它停在原地进行修理,那么后面的车就会被堵住没法行驶,交通就乱套了。幸好旁边有应急车道,可以把故障车辆推到应急车道修理,而正常的车流不会受到任何影响。等车修好了,再从应急车道回到正常车道即可。唯一的影响就是,应急车道用多了,原来的车辆之间的顺序会有点乱。
2.ES5 异步实现
- setTimeout + callback
function fn1ForLongTime(callback){}
function fn2ForShortTime(callback){}
将时间长的fn1改为异步,f2写成f1的回调函数
function fn1ForLongTime(callback){
setTimeout(function(){
//下面写f1的代码
callback()
},1000)
}
function fn2ForLongTime(){}
fn1ForLongTime(fn2ForLongTime)
执行顺序为:fn1发起异步,等fn1执行完毕后,再执行fn2.
形如f1(f2),而实际的f1代码与f2混在一起,能否支持f1(f2,f3)?
如果采用不停嵌套的方式来实现f1(f2,f3),代码可读性很差。
- 事件监听
容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
整个程序都要变成事件驱动型,运行流程会变得很不清晰··
f1.on('done', f2);
function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}
- 发布订阅模式
在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做["发布/订阅模式"]
//f2收到done的信号就可以继续
jQuery.subscribe("done", f2);
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
3.Promise是什么
Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。
每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。
作用:
(1)简化回调写法
(2)链式操作
3.1什么时候使用
-
One-and-Done Operations
- Asynchronous I/O operations: methods to read or write from a storage API could return a promise.
- Asynchronous network operations: methods to send or receive data over the network could return a promise.
- Long-running computations: methods that take a while to compute something could do the work on another thread, returning a promise for the result.
- User interface prompts: methods that ask the user for an answer could return a promise.
One-Time "Events"
因为promise对象提供了
pending:初始状态,也称为未定状态,就是初始化Promise时,调用executor执行器函数后的状态。fulfilled:完成状态,意味着异步操作成功。rejected:操作失败;
a resource such as an image, font, or even document, could provide a loaded property that is a promise that becomes fulfilled only when the resource has fully loaded (or becomes rejected if there’s an error loading the resource). Then, authors can always queue up actions to be executed once the resource is ready by doing resource.loaded.then(onLoaded, onFailure). This will work even if the resource was loaded already,More General State Transitions
还没有研究
3.2 什么时候别用
- Recurring Events
- Streaming Data
常见用法
- Promise构造函数
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,所以一般我们这么写:
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
resolve('随便什么数据');
}, 2000);
});
return p;
}
runAsync()
然后函数返回了promise对象,我们可以继续操作,在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出“随便什么数据”。
runAsync().then(function(data){
console.log(data);
//后面可以用传过来的数据做些其他操作
//......
});
//输出"执行完成" "随便什么数据"
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
then可以有两个参数
function getNumber(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
return p;
}
getNumber()
.then(
function(data){
console.log('resolved');
console.log(data);
},
function(reason, data){
console.log('rejected');
console.log(reason);
}
);
all的用法
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。
一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化