异步(Asynchronous)
指同一时间不止一个事件发生,或者说是多个相关事件不等待前一事件完成就发生。异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。
现在与将来
一个完整的javascript程序
,几乎一定是由多个块构成的。这些块中只有一个是现在执行,其余的则会在将来执行。最常见的块单位是函数。
举个例子
function now() {
return 21;
}
function later() {
answer = answer * 2;
console.log( "Meaning of life:", answer );
}
var answer = now();
setTimeout( later, 1000 );
上面的例子两个块:现在执行的部分,以及将来执行的部分
。
现在执行部分
function now() {
return 21;
}
function later() {
.. }
var answer = now();
setTimeout( later, 1000 );
将来执行部分
answer = answer * 2;
console.log( "Meaning of life:", answer );
任何时候,只要把一段代码包装成一个函数,并指定它在响应某个事件(定时器、鼠标点击、Ajax响应等)时执行,你就是在代码中创建了一个将来执行的块,也由此在这个程序中引入了异步机制。程序中现在运行的部分和将来运行的部分之间的关系就是异步编程的核心。
事件循环
异步与事件循环密切相关,在了解解决方案前,建议先看下并发模型与事件循环。
异步编程解决方案
- 回调
- 事件监听
- 发布订阅
- Promise
- Generator
- async/await
注意:Promise、Generator、async/await
在IE浏览器都不支持,需要做兼容处理。
下面介绍常用的解决方案。
1.回调
JavaScript 语言
对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。
缺点:大量的嵌套回调会造成回调地狱,难以维护。
// 如果下一请求依赖上一个请求的返回值,就需要不断嵌套
$.ajax({
url: "url-1",
success: function(){
$.ajax({
url: "url-2",
success: function(){
$.ajax({
url: "url-3",
success: function(){
// ...
}
});
}
});
}
});
2.promise(ES6)
介绍
- promise是一个代表了异步操作最终完成或者失败的对象。
- 本质上,promise是一个函数返回的对象, 它可以绑定回调函数
- Promise对象是一个构造函数,用来生成Promise实例。
- 使用promise最直接的好处就是能够使用then进行链式调用
创建
Promise 对象
是由关键字 new
及其构造函数来创建的。
var p = new Promise((resolve, reject) => {
// 一系列异步操作
// resolve()
// reject()
});
console.log(p) // Promise
想要某个函数拥有 promise
功能,只需让其返回一个promise即可。
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
};
状态
一个 Promise有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。状态:pengding=>fulfilled
- rejected: 意味着操作失败。状态:pending=>rejected
一个 promise 对象处在 fulfilled 或 rejected 状态而不是 pending 状态,那么它也可以被称为 settled 状态。你可能也会听到一个术语 resolved ,它表示 promise 对象处于 settled状态。但是在平时大家都习惯将 resolved 特指 fulfilled 状态
var p1 = new Promise((resolve, reject) => {
});
console.log(p1); // pending
var p2 = new Promise((resolve, reject) => {
resolve('成功');
});
console.log(p2); // fulfilled
var p3 = new Promise((resolve, reject) => {
reject('失败');
});
console.log(p3); // reject
注意:promise状态是不可逆的。
promise的状态 只有从pengding =》fulfilled 或者 pending =》 rejected,而不会反过来。并且已经resolve的数据,后面无论如何修改,都不会改变then中接受到的数据。
new Promise((resolve, reject) => {
var num = 100;
resolve(num);
num = 999;
resolve(num); // resolve 也不会改变已传出去的num 100
console.log(num) // 999
}).then(result => {
console.log(result) // 100
});