为什么需要异步?
因为基于JavaScript的语言自身的特点: Javascript语言的执行环境是单线程。即一次只能完成一个任务。若有多个任务则需排队逐个执行——前一个任务完成,再执行后一个任务。
为避免和解决这种问题,JS语言将任务执行模式分为异步和同步。同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;”异步模式”则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
前端常见的异步操作:
1、回调函数
也就是函数层层嵌套,参考自己文章:搞定callback-hell(回调地狱)、promise 回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。
2、Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了
Promise
对象。
两个特点:
- 1)对象的状态不受外界影响。
Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变 - 2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的
promise对象简单例子:
function timeOut(ms) {
const p = new Promise((resolve, reject) => {
setTimeout(resolve, ms, "done");
});
return p;
}
timeOut(2000).then(value => {
console.log(value); //两秒后打印 deon
});
timeout
方法返回一个Promise
实例,表示一段时间以后才会发生的结果。过了指定的时间(ms
参数)以后,Promise
实例的状态变为resolved
,就会触发then
方法绑定的回调函数。
⚠️: promise新建后会立即执行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
promise异步加载图片的例子:
function loadImg(url) {
const p = new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
console.log(img.width); //1280
};
img.onerror = () => {
reject(new Error("Could not load image at " + url));
};
img.src = url;
});
return p;
}
const url1 =
"https://user-gold-cdn.xitu.io/2020/3/27/1711ae8edcd5b389?imageView2/0/w/1280/h/960/format/webp/ignore-error/1";
console.log(loadImg(url1)); //Promise {<pending>}
上面代码中,使用Promise
包装了一个图片加载的异步操作。如果加载成功,就调用resolve
方法,否则就调用reject
方法。
promise对象实现Ajax:
// promise 实现ajax请求
function getAjax(url) {
const p = new Promise((resolve, reject) => {
const handler = function() {
if (this.readyState !== "4") {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return p;
}
getAjax("/posts.json").then(
function(json) {
console.log("Contents: " + json);
},
function(error) {
console.error("出错了", error);
}
);
3、async/await
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数是什么?一句话,它就是 Generator 函数的语法糖。
async
函数就是将 Generator 函数的星号(*
)替换成async
,将yield
替换成await
,仅此而已