当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有满足一定条件后该函数才能执行。
回调函数放在另外一个函数(eg:parent)的参数列表中,作为参数传递给parent,然后在parent函数的某个位置执行。
eg:定时器,Ajax中存在回调函数
同步任务在主线程上排队,只有前一个任务执行完毕,才能执行下一个任务。
异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。
不阻塞后面任务执行的任务就叫做异步任务。
在es6的Promise出来之前, 我们大多数时候是在使用回调函数(callback)和事件来处理一些异步问题。
事件: 某个对象的属性是一个函数, 当发生一件事时, 运行该函数
callback回调函数: 运行某个函数以实现某个功能的时候, 传入一个函数作为参数, 当某个特定的条件下的时候, 就会触发该函数
从本质上来说, 事件和回调函数也没有什么太大的区别, 仅仅就是函数放置的位置不太一样而已。
setTimeout(function() {
// 这个函数就是callback回调函数
}, 10)
$ajax({
url: '/api/getUserInfo',
success: function() {
// 成功的回调函数
},
error: function() {
// 失败的回调函数
}
})
var div = document.querySelector('#app');
div.addEventListener('click', function() {
// 点击div以后执行该函数
}, false)
setTimeout(function() {
console.log('执行了回调函数!');
},3000)
console.log('1111');
分析:
按照代码编写的顺序,
应该先输出“执行了回调函数”,
再输出“111”。
但实际执行结果为:
1111
执行了回调函数!
eg:我要说一句话,语序必须是下面这样的:”武林要以和为贵,要讲武德,不要搞窝里斗。”
setTimeout(function() {
console.log('武林要以和为贵');
setTimeout(function() {
console.log('要讲武德');
setTimeout(function() {
console.log('不要搞窝里斗');
},3000);
},2000)
},1000)
Promise对象的两个特点:
对象状态不受外界的影响。 Promise对象代表一个异步操作,有以下三个状态:
pending
:挂起状态(或者称之为等待状态), 未决阶段的状态, 代表事情还在未决, 结果还没出来fulfilled
:已处理(或者称之为处理成功), 已决阶段的状态, 代表事情已经产生结果, 而且这个结果是一个可以按照预定逻辑走下去的结果reject
:已拒绝(或者称之为处理失败), 已决阶段的状态, 代表事情已经产生结果, 但是这个结果跟预想的不太一样, 通常为发生错误拿一个网络请求来说, 请求的过程中为未决阶段: 状态为pedding, 而请求到了数据状态码为OK则是resolved状态, 而请求失败500服务器错误则是rejected状态
一旦状态改变,就不会再变任何时候都可以得到这个结果。 Promise对象的状态改变只有两种可能:
打印console.dir(Promise)
说明Promise是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
let promise = new Promise(function(resolve,reject){
if() { //异步操作成功
resolve (value)
}else {
reject (error)
}
})
Promise构造函数接受一个函数作为参数,这个函数的两个参数分别是resolve
和reject
。
/* runAsync返回一个Promise实例,表示一段时间后才会发生的结果 */
function runAsync() {
let p = new Promise((resolve,reject) => {
setTimeout(function() {
console.log('执行完成');
resolve('随便什么数据')
},2000);
})
return p;
}
/* 过了指定ms指定的时间后,Promise实例的状态就会变为resolved */
runAsync();
// 打印结果:
// 执行完成
/* 过了指定ms指定的时间后
Promise实例的状态就会变为resolved,
然后触发then方法指定的回调函数
Promise实例生成之后
可以用then方法来分别指定resolved状态和rejected状态的回调函数
then方法回调函数中的参数是resolve和reject函数残敌出来的参数 */
runAsync().then(function(data){
console.log(data);
})
// 打印结果:
// 执行完成
// 随便什么数据
let promise = new Promise((resolve, reject) => {
console.log('Promise');
resolve();
});
promise.then(() => {
console.log('Resolved');
});
console.log('Hi');
Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据。
then:接收两个参数, 一个thenable,意为绑定Promise成功的回调 一个catchable,意为Promise失败的回调 注册的两个函数何时执行由Promise的状态决定, 一旦Promise的状态走向已决的resolved状态则thenable函数执行, 走向rejected状态则catchabale执行
const myPromise = new Promise((resolve, reject) => {
// 我在这里直接触发resolve
resolve('我是成功要传递的数据');
})
myPromise.then((data) => {
console.log(data);
}, err => {
console.log(err);
})
catch:接收一个参数, 意为绑定Promise任务失败的回调, 一旦整个Promise的实例走向了rejected, catchable一定会执行
const myPromise2 = new Promise((resolve, reject) => {
reject('我是失败要传递的错误');
})
// then方法的第二个参数可以不传
myPromise2.then(data => {
console.log(data);
})
myPromise2.catch(err => {
console.log(err);
})
无论是then方法还是catch方法结束一个都会返回一个新的Promise实例
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调
用Promise.all来执行,all接收一个数组参数,里面的值最终都返回Promise对象。
function runAsync1() {
let p = new Promise((resolve,reject) => {
setTimeout(function() {
console.log('异步任务1执行完成');
resolve('随便什么数据1')
},2000);
})
return p;
}
function runAsync2() {
let p = new Promise((resolve,reject) => {
setTimeout(function() {
console.log('异步任务2执行完成');
resolve('随便什么数据2')
},2000);
})
return p;
}
Promise.all([runAsync1(), runAsync2()])
.then(function(results){
console.log(results);
});
function runAsync1() {
let p = new Promise((resolve,reject) => {
setTimeout(function() {
console.log('异步任务1执行完成');
resolve('随便什么数据1')
},2000);
})
return p;
}
function runAsync2() {
let p = new Promise((resolve,reject) => {
setTimeout(function() {
console.log('异步任务2执行完成');
resolve('随便什么数据2')
},2000);
})
return p;
}
Promise.race([runAsync1(), runAsync2()])
.then(function(results){
console.log(results);
});
Promise对于状态控制上的特点:
// Promise是一个构造函数,为了更好的封闭作用域,需要用到立即执行函数
const MyPromise = (function() {
// 常量变量定义
const PromiseStatus = Symbol('PromiseStatus'), // 每个实例上的Promise状态
PromiseValue = Symbol('PromiseValue'); // 每个实例上的Promise结果
// Promise状态, pedding, resolved, rejected
const _PEDDING = 'pedding';
const _RESOLVED = 'resolved';
const _REJECTED = 'rejected';
// 将真正的MyPromise构造函数返回出去
return class MyPromise {
// excutor:用户传递进来的函数参数
constructor(excutor){
// 每个Promise实例刚出生的时候状态为pedding, 结果为undefined
this[PromiseStatus] = _PEDDING;
this[PromiseValue] = undefined;
}
}
}());
阶段性实验代码:
const myPrimose = new MyPromise((resolve,reject) => {
})
console.log(myPrimose);
const MyPromise = (function() {
const PromiseStatus = Symbol('PromiseStatus'),
PromiseValue = Symbol('PromiseValue');
const _PEDDING = 'pedding';
const _RESOLVED = 'resolved';
const _REJECTED = 'rejected';
// resolve和reject方法, 我们在原型上或者构造函数中是看不到的
// 所以肯定resolve reject方法在闭包中
const resolve = _data => {
}
const reject = _error => {
}
return class MyPromise {
constructor(excutor){
this[PromiseStatus] = _PEDDING;
this[PromiseValue] = undefined;
// excutor在一开始就会被立马执行, 所以势必是在构造函数中走了一次
excutor(resolve,reject);
}
}
}());
阶段性测试:
const myPrimose = new MyPromise((resolve,reject) => {
console.log('我会立即执行吗?');
})
console.log(myPrimose);
const MyPromise = (function() {
const PromiseStatus = Symbol('PromiseStatus'),
PromiseValue = Symbol('PromiseValue');
const _PEDDING = 'pedding';
const _RESOLVED = 'resolved';
const _REJECTED = 'rejected';
// 定义一个this指向,避免当前作用域下的this混乱
let self = null;
/*
考虑到改变状态这个操作在resolve、reject中只有参数不同,我们可以抽离成一个方法
_status: 新的状态值
_result: 新的value值;
我们知道用户一旦将状态推向已决, 那么PromiseValue就一定要得出一个结果
*/
const changePromiseStatus = (_status,_result) => {
//当前阶段不是已决
if(self[PromiseStatus] !== _PEDDING) return;
self[PromiseStatus] = _status;
self[PromiseValue] = _result;
}
const resolve = _data => {
changePromiseStatus(_RESOLVED,_data);
}
const reject = _error => {
changePromiseStatus(_REJECTED,_error);
}
return class MyPromise {
constructor(excutor){
// this指向self
self = this;
this[PromiseStatus] = _PEDDING;
this[PromiseValue] = undefined;
excutor(resolve,reject);
}
}
}());
阶段性测试:
const myPrimose = new MyPromise((resolve,reject) => {
console.log('我会立即执行吗?');
resolve('hello');
})
console.log(myPrimose);
const mySecPromise = new MyPromise((resolve, reject) => {
console.log('我是第二个Promise实例');
reject('error');
})
console.log(mySecPromise);
// 对状态控制的最后一个处理
const MyPromise = (function() {
const PromiseStatus = Symbol('PromiseStatus'),
PromiseValue = Symbol('PromiseValue');
const _PEDDING = 'pedding';
const _RESOLVED = 'resolved';
const _REJECTED = 'rejected';
let self = null;
const changePromiseStatus = (_status,_result) => {
if(self[PromiseStatus] !== _PEDDING) return;
self[PromiseStatus] = _status;
self[PromiseValue] = _result;
}
const resolve = _data => {
changePromiseStatus(_RESOLVED,_data);
}
const reject = _error => {
changePromiseStatus(_REJECTED,_error);
}
return class MyPromise {
constructor(excutor){
self = this;
this[PromiseStatus] = _PEDDING;
this[PromiseValue] = undefined;
// 如果我们在executor中报错, 则会直接触发reject从而进入rejected状态,
// 并将错误信息传递给reject方便后续处理
// 所以我们势必需要try catch捕捉一下错误
try{
excutor(resolve,reject);
}catch(error){
reject(error)
}
}
}
}());
阶段性测试:
const myPrimose = new MyPromise((resolve,reject) => {
console.log('我会立即执行吗?');
resolve('hello');
})
console.log(myPrimose);
const mySecPromise = new MyPromise((resolve, reject) => {
console.log('我是第二个Promise实例');
console.log(abc); // 未声明会报错
})
console.log(mySecPromise);
毫无意外, 结果如下, 如果在executor中报错, 则会直接进入rejected状态
then方法
接收两个参数, 第二个参数可以不传, 这两个参数分别是成功后的回调和失败后的回调,catch方法
接收一个参数, 为失败后的回调。
const MyPromise = (function() {
const PromiseStatus = Symbol('PromiseStatus'),
PromiseValue = Symbol('PromiseValue');
const _PEDDING = 'pedding';
const _RESOLVED = 'resolved';
const _REJECTED = 'rejected';
// 用来存储Promise.then加入的回调
const fullFilledList = Symbol('fullFilledList');
// 用来存储Promise.catch
const rejectedList = Symbol('rejectedList');
// 这个方法时用来处理用户调用then和catch方法时的处理函数
// _status: 要判定的状态, handler: 当前处理函数, queue: 当前处理函数队列
const settleHandler = (_status, handler, queue) => {
if(self[PromiseStatus] === _status) {
setTimeout(() => handler(self[PromiseValue]), 0);
}
else queue.push(handler);
}
let self = null;
const changePromiseStatus = (_status,_result) => {
if(self[PromiseStatus] !== _PEDDING) return;
self[PromiseStatus] = _status;
self[PromiseValue] = _result;
// 一旦用户更改了状态, 所有在then和catch中对应的方法都要执行
if(self[PromiseStatus] === _RESOLVED) self[fullFilledList].forEach(ele => ele(self[PromiseValue]))
else self[rejectedList].forEach(ele => ele(self[PromiseValue]))
}
const resolve = _data => {
changePromiseStatus(_RESOLVED, _data);
}
const reject = _error => {
changePromiseStatus(_REJECTED, _error);
}
return class MyPromise {
constructor(executor) {
self = this;
this[PromiseStatus] = _PEDDING;
this[PromiseValue] = undefined;
this[fullFilledList] = [];
this[rejectedList] = [];
try {
executor(resolve, reject);
}catch(error) {
reject(error);
}
}
// then 和catch方法是在原型上出现的
// then方法接受一个thenbale: 成功回调函数和一个catchable: 失败回调函数
then = (thenable, catchable) => {
// 我们知道, 如果当前状态已经为已决阶段的两种状态了, 那么回调函数
// 会被立即执行, 否则才会放入相应数组
// 设置相应状态
settleHandler('resolved', thenable, this[fullFilledList])
// 因为catchable很有可能不传递, 所以必须容错
typeof catchable === 'function' && this.catch(catchable);
}
// catch方法只接受一个catchable失败回调函数
catch = (catchable) => {
settleHandler('rejected', catchable, this[rejectedList])
}
}
}())
阶段性测试:
const myPromise = new MyPromise((resolve, reject) => {
console.log('我会立即执行吗?');
reject('hello');
})
myPromise.then((data) => {
console.log(data);
}, (err) => {
console.log(err);
})
console.log(myPromise);
Promise.all()
的实现Promise.all 接收一个 promise 对象的数组作为参数,当这个数组里的所有 promise 对象全部变为resolve或 有 reject 状态出现的时候,它才会去调用 .then 方法,它们是并发执行的。
promise.all()
特点解读:
Promise.all()方法可以将多个Promise实例包装成为一个Promise对象§,接收一个数组作为参数,数组中不一定需要都是Promise对象,但一定具有Iterator接口。如果不是,调用Promise.resolve将其转化为Promise对象之后再进行处理。
使用Promise.all()生成的Promise对象§的状态由数组中的Promise对象(p1,p2,p3)决定:
手写实现自己的Promise.all()
function PromiseAll(arr) {
//PromiseAll的返回值为一个Promise对象
return new Promise((resolve,reject) => {
//传入的PromiseAll必须是一个数组
if(!Array.isArray(arr)){
return reject(new TypeError('arr must be an array.'));
};
let resArr = [];
for(let i in arr) {
(function(i){
Promise.resolve(arr[i]).then(res => {
resArr.push(res);
if(i == arr.length-1){
return resolve(resArr);
}
},err => {
return reject(err);
}).catch(err => {
console.log(err);
})
})(i)
}
})
}
function PromiseAll(arr) {
return new Promise((resolve,reject) => { // 返回一个新的Promise对象
let resArr = []; // 定义一个空数组存放结果
let i = 0;
function handleData(index,data){ // 处理数据函数
resArr[index] = data;
i++;
if(i == arr.length){ //当i等于传递的数组的长度时
resolve(resArr); //执行resolve,并将结果放入
}
}
for(let i=0;i<arr.length;i++) { //循环遍历数组
Promise.resolve(arr[i]).then((data) => {
handleData(i,data); //将结果和索引传入handleData函数
})
}
})
}
测试:
// 测试
const pro1 = new Promise((res,rej) => {
setTimeout(() => {
res('1')
},1000)
})
const pro2 = new Promise((res,rej) => {
setTimeout(() => {
res('2')
},2000)
})
const pro3 = new Promise((res,rej) => {
setTimeout(() => {
res('3')
},3000)
})
const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => {
console.log(res);// 3秒之后打印 ["1", "2", "3"]
}).catch((e) => {
console.log(e);
})
Promise.race()
的实现function PromiseRace(arr) {
return new Promise((resolve,reject) => {
if(!Array.isArray(arr)){
return reject(new TypeError('arguments must be Array'));
};
for(let i=0;i<arr.length;i++) {
Promise.resolve(arr[i]).then(data => {
resolve(data);
},err => {
reject(err);
})
}
})
}
测试:
// 测试
const pro1 = new Promise((res,rej) => {
setTimeout(() => {
res('1')
},1000)
})
const pro2 = new Promise((res,rej) => {
setTimeout(() => {
res('2')
},2000)
})
const pro3 = new Promise((res,rej) => {
setTimeout(() => {
res('3')
},3000)
})
const proAll = PromiseRace([pro1,pro2,pro3])
.then(res => {
console.log(res); // 输出1
}).catch((e) => {
console.log(e);
})
手写Promise参考博客