前言
本次分享的内容是 Promise、Generator 函数与 async/await ,都是与异步操作相关的内容,我们常见的异步操作基本是:回调函数和事件监听,AJAX就是事件监听的一种,本次分享中也主要以AJAX为例。
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.responseText);
} else {
return fail(request.status);
}
}
}
request.open('GET', '/api/categories');
request.send();
Promise
首先先看一下Promise是什么:
Promise.png可以看到Promise是一个构造函数,里面有我们熟悉的all、 reject 、resolve 方法,原型上有 then、catch 方法,我们可以新建一个Promise 对象来使用。
new Promise(
/* executor */
function(resolve, reject) {...}
);
我觉得文档上说的比我清楚的多:
promises.pngPromise对象是一个代理对象(代理一个值)。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers )。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象
一个 Promise有以下几种状态:
- pending: 初始状态,不是成功或失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
因为 Promise.prototype.then 和 Promise.prototype.catch
方法返回promise 对象, 所以它们可以被链式调用。
疑问
既然 .then() 既有reject 也有 resolve的回调,那为什么还有.catch()?
我看网上的说法是说 如果是 .then() 中报错,则会进入 .catch() 中处理,这样可以避免报错,类似于 try catch。
基本api
- Promise.resolve()
- Promise.reject()
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.all() // 所有的完成
- Promise.race() // 竞速,完成一个即可
应用
然后我们用Promise来重写一下ajax:
function ajax (method, url, data) {
var request = new XMLHttpRequest();
return new Promise(function (resolve, reject) {
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(request.status);
}
}
};
request.open(method, url);
request.send(data);
});
}
var p = ajax('GET', '/api/xxxx');
p.then(function (text) {
console.log(text);
}).catch(function (status) {
console.log('ERROR: ' + status);
});
看起来好像并没有什么改变,但是如果我们想一个请求完成再发起另一个请求呢?
ajax ('GET', '/api/hhh').then(function(data) {
return ajax('POST', '/api/hhhh', data.body);
})
.then(function(data) {
return ajax('POST', '/api/hhhhh', data.body);
})
.then(function() {
console.log(data);
});
肯定比无限回调好看多了。
除此之外,我们可以使用 Promise.all() 来并行执行异步操作。
function func(num){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
resolve(num);
}, 1000);
});
return p;
}
Promise.all([func(1), func(2), func(3)])
.then(function(results){
console.log(results);
});
// console的值为 [1, 2, 3]
三个异步操作的并行执行的,等到它们都执行完后才会进到then里面,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。
Generator 函数
之前没怎么接触过,是一个可以返回多次的函数。
generator 函数由 function*定义,除了 return 还可以使用 yield 返回多次。那到底怎么用?
应用
先来个简单的例子:
斐波那契数列,一般我们要这样写:
function fib(max) {
var
t,
a = 0,
b = 1,
arr = [0, 1];
while (arr.length < max) {
t = a + b;
a = b;
b = t;
arr.push(t);
}
return arr;
}
fib(5); // [0, 1, 1, 2, 3]
使用 generator:
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 1;
while (n < max) {
yield a;
t = a + b;
a = b;
b = t;
n ++;
}
return a;
}
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: true}
//
网友评论