JS异步编程的那些事(一)

      从最早的回调函数,之后迎来了 promise,再次就是上篇文章说的 generator,js 解决异步的方案一次次的再优化,ES7 中出现的 Async/await 是目前 js 中异步最终极的解决方案,我的妈耶不谈兼容问题的话,实在是太好用啦。虽然兼容性真的很一般,但是我们可以用 polyfill(js 修补器)去兼容一下呀。接下来我将简单介绍下 Promise、fetch、 Async/await 以及分别用 fetch 和 Async/await 去封装一个增删改查的方法。来看看编写异步代码的过程是如何一步步变得优雅起来的。

Promise

      首先我们先来说说 Promise 吧~在 ES6 之前处理异步问题时,我们为了避免操作时的页面中断,通过都是使用回调函数,这时候如果回调函数当中又有回调函数,那么我们就会陷入无尽的回调地狱,代码也变得很难维护。而有了 Promise 对象后,我们就可以很好的解决这个问题,用它的链式调用就可以用同步函数的方式去写异步代码了。简直是造福程序员界一项很牛逼的东西。接下来我们创建一个 Promise 实例。

1
2
let promise = new Promise();
console.log(promise); //Uncaught TypeError: Promise resolver undefined is not a functionat new Promise

      注意:Promise 构造函数一定要传回调才可以,否则会报错。它接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。

1
2
3
let promise = new Promise((resolve, reject) => {
});
console.log(promise); //Promise {<pending>}

      我们看到打印出来的是Promise{ < pending > },表示任务在进行中。接下来我将用一张图来看看 Promise 的三种状态和对应的回调。

Promise 的三种状态和对应的回调


Promise的三种状态和对应的回调

      虽然这张图我画的有点丑,但是应该也很简单明了的看懂Promise的三种状态,其中我们基本上运用的是resolved和resolved两种状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let promise = new Promise((resolve, reject) => {
if (/* 异步操作成功*/) {
resolve();
} else {
/* 操作失败 */
//reject();
}
});
promise.then(() => {
console.log("成功,没有任何问题"); //成功,没有任何问题
}).then(() => {
console.log("成功,想调几次就调几次");
}).catch(() => {
console.log("执行失败方法");
});

      我们可以看到,只要前面的reject()成功,后续想调用几个then()方法都可以,这样就不仅仅有一个回调了,有n个回调在等着你。如果异步操作失败了,Promise的转态就会变为rejected,去调用catch方法指定的回调函数处理这个错误。另外,then方法指定的回调函数,如果在运行过程中抛出了错误,也会被catch方法捕获。这里要注意的是:catch返回码超过300就不会打印

Promise 对象特点

1.对象的状态是不受外界影响的

只有异步操作的结果,才可以决定当前是哪一种状态,任何其他的操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
let fulfill = new Promise((resolve,reject) => {
resolve('success');
console.log('resolve before');
reject('error');
});

fulfill.then(data => {
console.log(data);
});

fulfill.catch(msg => {
console.log(msg);
})

2.一旦转态改变,转态就会凝固,不再改变

最后的打印结果为:
resolve before
success
resolve 下一句语句是可以执行的,为什么 reject 没有去调用 catch 指定的回调函数呢?这就是因为 Promise 对象的特点:状态的凝固。这个对象从起始的 Pending 状态在根据 resolve 或 reject 返回 Resolved 或 Rejected 状态。

fetch

      fetch 是一个基于 promise 的请求方法,更简单便捷。相比与原先在不同浏览器中使用 XMLHttpRequest 对象或者是 ActiveXObject(“x.XMLHTTP”),fetch 方法简化了这一操作。接下来我们用代码看看 fetch 是如何让代码优雅起来的。

fetch 如何优雅于 XMLHttpRequest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//XMLHttpRequest
var xml = new XMLHttpRequest;
//调用请求
xml.open('GET', 'http://jsonplaceholder.typicode.com/postst');
//发送到服务器上
xml.send();
//监听响应情况
xml.onreadystatechange = function () {
//响应成功事件
if (this.readyState === 4 && xml.status === 200) {
console.log(xml.responseXML);
} else {
console.log("发生错误:", xmlJson.status);
}
};
1
2
3
4
5
6
//fetch
let url = "http://jsonplaceholder.typicode.com/posts";
fetch(url)
.then((res) => {
console.log(res); //Response {type: "cors", url: "http://jsonplaceholder.typicode.com/ posts", redirected: false, status: 200, ok: true, …}
}).catch();;

      从代码上很直观的能看出代码量的减少。fetch 方法返回的是一个 Promise 对象,所以我们可以链式的发起异步请求。http://jsonplaceholder.typicode.com 这个是我做测试经常会用到的网址。从上面的例子可以看出使用 fetch 时第一步 then 返回的是 response 对象。我们需要使用json()方法将将请求回来的 response 对象解析成我们正常可读的对象。这里要注意的是:目前原生的 fetch 还不支持 jsonp 的请求方式,如果需要实现 jsonp,需要安装 npm 包 fetchJ-jsonp

1
2
3
4
5
6
7
8
9

let url = "http://jsonplaceholder.typicode.com/posts";
fetch(url)
.then((res) =>
//将请求回来的 response 对象解析成我们正常可读的对象
res.json()
).then((data) => {
console.log(data);
}).catch();;

      我们在 response 对象上调用 json()方法,返回的依然是一个 Promise 对象,我们需要在下一步的 then()中获得服务器返回的原始对象。接下来我们去请求一个根本没有的域名,模拟一下 catch。

1
2
3
4
5

let errurl = "http://jsonplaceholder.typicode12345.com/posts";
fetch(errurl)
.then(() => console.log("成功"))
.catch((error) => console.log(`错误信息:${error}`));//错误信息:TypeError: Failed to fetch

fetch 请求三种数据格式

      接下来,我们来实践下如何用 fetch 请求 本地文本数据,本地 json 数据 还有网络接口吧。具体的代码实现效果可以看看实现效果。其中请求本地文本数据采用了 XMLHttpRequest 和 fetch 两种方式。这里展示一部分重要代码。样式没有特意去优化,用的是一个超简单的响应式模板 skeleton

请求本地文本数据

1
2
3
4
5
6
7
8
9
10
function getText() {
fetch("data/test.txt")
//将 response 对象解析
.then((res) => res.text())
.then((data) => {
console.log(data); //变瘦瘦变美美
document.getElementById("output").innerHTML = data;
})
.catch(() => { console.log("获取失败") });
}

请求本地 json 数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getJson() {
fetch("data/test.json")
.then(res => res.json())
.then((data) => {
console.log(data);//(6) [{…}, {…}, {…}, {…}, {…}, {…}]
let outPut = '';
//使用forEach方法遍历数据
data.forEach((x) => {
outPut += `
id:${x.id}<br>
title:${x.title}<br>
body:${x.body}<br>
`
});
document.getElementById("output").innerHTML = outPut;
})
.catch(() => { console.log("获取失败") });
}

请求网络接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getWeb() {
let weburl = "https://api.github.com/users";
fetch(weburl)
.then((res) => res.json())
.then((data) => {
console.log(data);
let webData = '';
data.forEach((x) => {
webData += `loginName:${x.login}<br>`
});
document.getElementById("output").innerHTML = webData;
})
.catch(() => {
console.log("获取失败");
});
}

      本篇主要介绍记录了 Promise 对象,fetch 如何优雅于 XMLHttpRequest,以及如何用 fetch 请求三种数据数据格式。下一篇中,我们将用 fetch 去封装一个增删改查的方法,以及记录下 JS 最终极的异步解决方案 Async/awai。

Copyright ©2019 guowj All Rights Reserved.

访客数 : | 访问量 :