在阮一峰的ES6中是这样解释promise的 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
一.回调函数
一个回调函数,也被称为高阶函数;也就是把一个B函数当成一个参数传递给另一个A函数。B函数需要在A函数中进行调用,这个B函数就叫回调函数。A函数执行完成之后B函数才会执行。这个过程是回调。
注意:回调函数不是立即就执行。它是在另一个函数执行完成之后被调用,即在包含的函数体中指定的地方调用。
//第一种情况:
$("button").on("click", () => {
alert("hello")
})
/*
在jQuery这个点击事件的方法中,我们传递了一个函数作为参数传递给了click
*/
//第二种情况:
[1,2,3].forEach((item,index,obj)=>{
console.log(item)
})
/*
同理,在ES5新增的数组方法中,都是接收一个函数作为参数
*/
//第三种情况:
function A(callback) {
callback();
console.log("我是主函数")
}
function B() {
setTimeout(() => {
console.log("我是回调函数")
}, 2000)
}
A(B)
/*
按顺序先执行回调函数,但输出结果却是后输出回调函数内容,A函数不用等回调函数执行完再执行后续的语句,可以执行自己的代码,等回调函数准备好,再执行回调函数。所谓异步加载也不过如此。因此可以证明异步与回调并没有直接的联系,回调只是异步的一种实现方式
*/
function syncfn(callback) {
callback()
}
syncfn(function() {
alert("我是同步函数里的函调函数")
})
function async(callback) {
setTimeout(function() {
callback()
}, 1000)
}
async(function() {
alert("我是异步函数里的回调函数")
})
/*
这个例子说明异步和回调函数没有直接的联系,回调只是异步的一种实现方式
*/
二 promise
1.为什么需要promise 比如我们再等待一个请求结果的时候,需要上一条ajax请求的结果,那我们就会产生回调套回调的情况:
//这里我们使用定时器来,模拟一下,
setTimeout(function() {
console.log(1)
setTimeout(function() {
console.log(2)
setTimeout(function() {
console.log(3)
}, 1000)
}, 1000)
}, 1000)
//我们这里写一段伪代码,在实际场景下我们很容易看到这种情况,如果依赖的层级关系更加多的话,就很容易造成回调地狱
$.ajax({
type: "method",
url: "url",
data: "data",
dataType: "dataType",
success: function (response) {
$.ajax({
type: "method",
url: "url",
data: "data",
dataType: "dataType",
success: function (response) {
$.ajax({
type: "method",
url: "url",
data: "data",
dataType: "dataType",
success: function (response) {
}
});
}
});
}
});
这时,我们可能会希望:
让代码变得更具有可读性和可维护性,减轻一层层套用数据和请求的现象;
将请求和数据处理明确的区分开;
这样我们的promise就登场了,promise中的then方法,可以解决刚刚出现的恐怖的回调函数问题,让代码更加优雅;
2.promise的API
构造函数属性:
1)本身就是一个构造函数,具有constructor属性,并且指针指向它的构造函数promise
2) Promise 新建后就会立即执行。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
实例方法
1).promise.then() 成功的时候调用
2).promise.catch() 失败的时候调用
promise.then成功和失败时都可以使用,并且then方法的执行结果也会返回一个Promise对象。
promise.then()的第二个参数和promise.catch()捕获错误的区别:
1.promise.then()的第二个参数可以捕获到
如果想要捕获到图1中的错误,那么就需要使用try{}catch(err){}的方法进行捕获;
2.promise.catch()的是可以捕获到.then()方法中的错误信息的。
静态方法:
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,当所有的promise对象有返回值的时候,同时返回
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例,返回的是第一个改变promise实例。
Promise.resolve() 有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
Promise.resolve()等价于下面的写法。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.reject() Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
promise 对象的特点:
1)对象的状态不受外界影响,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变
2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
3:promise的状态:
const p1 = new Promise(function(resolve, reject) {}); // 进行中
const p2 = new Promise(function(resolve, reject) {
resolve(p1);
})
p2.then(res => {
console.log("p2")
})
因为p1一直没有执行所以,p2没有返回值
let p1 = new Promise((resolve, reject) => {
if (false) {
resolve("成功")
} else {
reject("失败")
}
})
let p2 = new Promise(function(resolve, reject) {
resolve(p1)
})
p2.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
p1的状态决定着p2的状态
三 promise的使用
1:promise的执行顺序
var p = new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(() => {
console.log('执行完成');
resolve('我的数据');
}, 0);
console.log("我先执行")
});
//先输出:我先执行
//1秒之后输出:执行完成
我们这里简单可以理解:
异步任务:指不进入主线程,而是进入“任务队列的任务”,只有等主线程任务执行完毕,“任务队列”开始通知主线程,请求执行任务,该任务才会进入主线程执行。
同步任务:指在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
promise的链式操作和数据传递:
function fn(num) {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(num)
}, 1000)
})
}
fn(1).then(res => {
console.log(res)
return fn(++res)
}).then(res => {
console.log(res)
return fn(++res)
}).then(res => {
console.log(res)
})