promise的学习笔记-ES6系列1

Promise的学习参考了以下几位大大们的博客~
https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544
https://segmentfault.com/a/1190000007032448
http://es6.ruanyifeng.com/#docs/promise

1. 同步和异步

Promise是异步编程的一种解决方案,所以我们应该先来了解一下什么是同步和异步。

1.1 同步

同步的方法指的是方法一旦开始调用,调用者必须等到方法调用结束后,才可继续后续的行为。

console.log("开始")
while(1)
console.log("结束")

第二个打印语句是无法被打印出来的,因为这是一个同步模式,while死循环阻塞了进程。

1.2 异步

异步模式可以执行多个任务,如果任务A需要等待,可以先执行任务B,等到任务A结束后再继续进行函数的回调。
常见的异步模式有定时器、ajax请求。

console.log("开始")
setTimeout(function(){
	console.log("定时器中的异步任务")
},0)//0毫秒后开始执行定时器内的回调函数
console.log("结束")

输出如下所示
在这里插入图片描述
我们可以看到,即使定时器的延迟时间为0。还是会先执行完同步的任务再执行定时器内的异步任务。即:异步任务会在当前脚本的所有同步任务执行完才会执行。

2. Promise

了解完同步和异步,我们就可以深入的了解一下Promise。

2.1 基本概念

1.Promise对象代表一个异步操作,具有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
2.Promise对象的状态改变,只能有两种可能:(1)pending–>fulfilled [称为resolved状态] (2)pending–>rejected [称为rejected状态]。
3.一旦状态发生了改变,状态就凝固了,会一直保持这个状态,不会再发生变化。

2.2 基本用法

在ES6中,Promise对象是一个构造函数,用来生成Promise实例。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve和reject。这两个函数就是就是「回调函数」,由JavaScript引擎提供。

resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成后,可以使用then方法指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // resolved时调用
}, function(error) {
  // rejected时调用
});

其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

let promise = new Promise(function(resolve, reject) {
  console.log('Promise立即执行');
  resolve();
});

promise.then(function() {
  console.log('resolved打印输出');
});

console.log('同步打印输出');

在这里插入图片描述
可以看到,promise对象新建后立即执行。而在then方法中指定的回调函数,将在当前脚本所有同步任务执行完后才会进行。所以resolve打印输出在最后执行。

3. Promise对象的基本API

3.1 .then()

语法:Promise.prototype.then(onFulfilled, onRejected)
具体用法如上所述

3.2 .catch()

语法:Promise.prototype.catch(onRejected)
该方法是.then(undefined, onRejected)的别名,用于指定发生错误时的回调函数。

promise.then(function(data){
	console.log('success');
}).catch(function(error){
	console.log('error',error);
});

等同于

promise.then(function(data){
	console.log('success');
}).then(undefined,function(error){
	console.log('error',error);
});

(有需要添加的内容)

3.3 .all()

语法:Promise.all(iterable)
该方法用于将多个Promise实例,包装成一个新的Promise实例。

Promise.all方法接受一个数组(或具有Iterator接口)作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用Promise.resolve转换为一个promise)。它的状态由这三个promise实例决定。

当p1, p2, p3状态都变为fulfilled,p的状态才会变为fulfilled,并将三个promise返回的结果,按参数的顺序(而不是 resolved的顺序)存入数组,传给p的回调函数

var p1 = new Promise(function(resolve,reject){
	setTimeout(resolve, 3000, "p1");
});
var p2 = new Promise(function(resolve,reject){
	resolve("p2");
});
var p3 = new Promise((resolve,reject)=>{
	setTimeout(resolve, 1000, "p3")
});
Promise.all([p1,p2,p3]).then(function(values){
	console.log(values);
});

在这里插入图片描述
当p1, p2, p3其中之一状态变为rejected,p的状态也会变为rejected,并把第一个被reject的promise的返回值,传给p的回调函数

var p1 = new Promise(function(resolve,reject){
	setTimeout(resolve, 1000, "p1");
});
var p2 = new Promise(function(resolve,reject){
	setTimeout(reject, 2000, "p2");
});
var p3 = new Promise((resolve,reject)=>{
	reject("p3")
});
Promise.all([p1,p2,p3]).then(function(values){
	console.log("resolve",values);
},function(error){
	console.log("reject",error);
});

在这里插入图片描述
可以看到 虽然p2,p3都被reject了,但第一个被reject的promise是p3,所以返回的是第一个被reject的promise的返回值。

3.3 .race()

语法:Promise.race(iterable)
Promise.race方法同样接受一个数组(或具有Iterator接口)作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilled或rejected),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数。

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "p1"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "p2"); 
});

Promise.race([p1, p2]).then(function(value) {
    console.log('resolve', value); 
}, function(error) {
    console.log('reject', error); 
});

在这里插入图片描述
当数组中的promise对象race后,并不会取消其他promise对象的执行。

var fastPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('fastPromise');
        resolve('resolve fastPromise');
    }, 100);
});
var slowPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('slowPromise');
        resolve('resolve slowPromise');
    }, 1000);
});
// 第一个promise变为resolve后程序停止
Promise.race([fastPromise, slowPromise]).then(function (value) {
    console.log(value);    // => resolve fastPromise
});

在这里插入图片描述

3.4 .resolve()

语法:
Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(thenable);

Promise.resolve('Success')

等同于

new Promise(function(resolve){
	resolve('Success');
})

(1)当参数为promise实例时
不做任何修改,原封不动地返回这个实例。
(2)当参数为thenable对象(具有then方法的对象)
Promise.resolve方法会将这个对象转换为Promise对象,然后立即执行then方法;

let thenable = {
	then:function(resolve,reject){
		resolve("then console")
	}
}
let p1 = Promise.resolve(thenable);
p1.then(function(value){
	console.log(value);
})

在这里插入图片描述
使用了Promise.resolve()后,立即执行了then方法中定义的回调函数。
(3)当参数为不具有then方法的对象 或 不是对象
Promise.resolve方法返回一个新的Promise对象,状态为Resolved

var p1 = Promise.resolve('Success');

p1.then(function(value){
	console.log(value);
})

在这里插入图片描述
通过Promise.resolve方法,p1成为了一个新的Promise实例,且状态为Resolved,所以会立即调用then中定义的回调函数。

3.5 .reject()

语法:Promise.reject(reason)
用法与Promise.resolve()类似,也是new Promise()的快捷方式,不过会让Promise对象立即进入rejected状态,立即执行then中指定的onRejected回调函数。

3.6 .finally()

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "p1"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "p2"); 
});

Promise.race([p1, p2]).then(function(value) {
    console.log('resolve', value); 
}, function(error) {
    console.log('reject', error); 
}).finally(function(){
    console.log("always Finally")
});

在这里插入图片描述

4. 为什么使用Promise

4.1 回调地狱

我们假设我们要知道小明的奶奶叫什么。而现在我们只能从小明的学校那(接口1)拿到小明老师的联系方式(成功data1),再从小明的老师那(接口2)拿到小明爸爸的联系方式(成功data2),最后从小明爸爸那(接口3)拿到了小明奶奶的名字(成功data3)。但其中也有出错的可能,比如小明爸爸忘了奶奶名字(失败error3),小明老师没有爸爸联系方式(失败error2),小明学校没有小明老师联系方式(失败error1)。
用原生的ajax来写上面的请求,是下面这样的

request('test1.html', '', function(data1) {
    console.log('小明老师的联系方式:', data1);
    request('test2.html', data1, function (data2) {
        console.log('小明爸爸的联系方式:', data2);
        request('test3.html', data2, function (data3) {
            console.log('小明奶奶的名字为:', data3);
            //request... 继续请求
        }, function(error3) {
            console.log('小明爸爸忘了奶奶名字:', error3);
        });
    }, function(error2) {
        console.log('小明老师没有爸爸联系方式', error2);
    });
}, function(error1) {
    console.log('小明学校没有老师的联系方式:', error1);
});

我们可以看到,多层回调层层嵌套,让人晕头转向,这就是所谓的回调地狱。

4.2 Promise逃离回调地狱

我们来试一下用Promise来重写上例

sendRequest('test1.html', '').then(function(data1) {
    console.log('小明老师的联系方式:', data1);
    return sendRequest('test2.html', data1);
}).then(function(data2) {
    console.log('小明爸爸的联系方式:', data2);
    return sendRequest('test3.html', data2);
}).then(function(data3) {
    console.log('小明奶奶的名字为:', data3);
}).catch(function(error) {
    //用catch捕捉前面的错误
    console.log('请求失败了, 失败原因为:', error);
});

5. Promise的注意点

5.1 reject和catch的区别

promise.then(onFulfilled, onRejected)
在onFulfilled中发生异常的话,在onRejected中是捕获不到这个异常的。
promise.then(onFulfilled).catch(onRejected)
.then中产生的异常能在.catch中捕获
一般情况,建议使用第二种,可以捕获之前所有的异常。

5.2 then中抛错必须在catch中捕获

如果在then中抛错,而没有对错误进行处理(即catch),那么会一直保持reject状态,直到catch了错误。

function taskA() {
    console.log(x);
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask() {
    console.log("Final Task");
}
var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask);

在这里插入图片描述
可以看到taskA抛出错误后,状态一直维持在reject的状态,taskB是不会执行的。直到catch了错误,才继续执行finalTask。

5.3 Promise的链式调用

每次调用then都会返回一个新创建的Promise对象,而then内部只是返回的数据。
(1)对同一个Promise对象同时调用then方法

var p1 = new Promise(function(resolve){
	resolve(100);
});
p1.then(function(value){
	return value * 2;
});
p1.then(function(value){
	return value * 2;
});
p1.then(function(value){
	console.log("finally" + value);
});

在这里插入图片描述
可以看到,then的调用是同时开始执行的,且传给每个then的value值都是100。
(2)使用链式调用then方法

var p2 = new Promise(function(resolve){
	resolve(100);
})
p2.then(function(value){
	return value*2;
}).then(function(value){
	return value*2;
}).then(function(value){
	console.log("finally"+value)
})

在这里插入图片描述

5.4 Promise状态的凝固

promise状态变为resolve或reject,就凝固了,不会再发生改变。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值