Promise异步编程
一、什么是Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
以下是MDN对Promise的定义
The Promise object is used for asynchronous computations. A Promise represents a single asynchronous operation that hasn't completed yet, but is expected in the future.
译文:Promise对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作。
二、同步和异步
我们知道,JavaScript的执行环境是「单线程」。
所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,它会「阻塞」其他任务。这个任务可称为主线程。
但实际上还有其他线程,如事件触发线程、ajax请求线程等。 这也就引发了同步和异步的问题。
同步:
while(true);
console.log("don't execute"); //不会执行
异步:
setTimeout(function() {
console.log('taskA, asynchronous');
}, 0);
console.log('taskB, synchronize');
三、回调函数
回调函数是一段可执行的代码段,它以「参数」的形式传递给其他代码,在其合适的时间执行这段(回调函数)的代码。
var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});
上面例子中的setTimeout就是一个回调函数
再看一次:
setTimeout(function() {
console.log('taskA, asynchronous');
}, 0);
console.log('taskB, synchronize');
四、为什么使用Promise
使用上面的回调函数的方式,如果回调函数嵌套很多层,很容易陷入回调地狱,代码可读性差
function fn(callback1, callback2) {
// 耗时操作 let a = 0
for (let i = 0; i < 100; i++) {
a++
}
setTimeout(function() {
callback1(++a)
setTimeout(function() {
callback2(++a)
}, 0)
}, 0)
}
function fn1(a) {
console.log(a)
}
function fn2(a) {
console.log(a)
}
fn(fn1, fn2)
五、Promise基本概念
Promise对象代表一个未完成、但预计将来会完成的操作。
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
它有以下三种状态:
pending:初始值,不是fulfilled,也不是rejected
fulfilled:代表操作成功
rejected:代表操作失败
Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。
一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。
当状态发生变化,promise.then绑定的函数就会被调用。
注意:Promise一旦新建就会「立即执行」,无法取消。
六、Promise基本用法
下面代码创造了一个Promise实例。
const promise = new Promise(
// executor
function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
executor是带有 resolve 和 reject 两个参数的函数 。Promise构造函数执行时立即调用executor 函数, resolve 和 reject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) { // success }, function(error) { // failure }); then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
七、Promise实例应用
//简单的promise
const promise1 = new Promise(
// executor
function(resolve, reject) {
if (false){
resolve('request success');
} else {
reject('request error');
}
});
promise.then(function(value) {
console.log(value);
}, function(error) {
console.log(error);
});
//then只传一个方法的情况
const promise2 = new Promise(
// executor
function(resolve, reject) {
if (false){
resolve('request success');
} else {
reject('request error');
}
});
promise2.then(function(value) {
console.log(value);
});
promise2.catch(function(error) {
console.log(error);
});
//链式调用
const promise3 = new Promise(
// executor
function(resolve, reject) {
if (false){
resolve('request success');
} else {
reject('request error');
}
}).then(function(value) {
console.log(value);
}).catch(function(error) {
console.log(error);
});
//嵌套调用
const promise4 = new Promise(
// executor
function(resolve, reject) {
if (true){
resolve('request success');
} else {
reject('request error');
}
}).then(function(value) {
const nextPromise = new Promise(
function(resolve, reject) {
resolve('next request success');
});
return nextPromise;
}).then(function(value) {
console.log(value);
}).catch(function(error){
console.log(error);
});