Promise对象的一些知识点

Promise对象

1.基本用法

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

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

Promise构造函数接受一个函数作为参数,该函数的俩个参数分别为resolve和reject,他们是俩个函数,由JS引擎提供,不用配置。

  • resolve :异步操作执行成功后的回调函数

  • reject:异步操作执行失败后的回调函数

Promise实例生成后可以用then方法分别指定Resolved状态和Rejected状态的回调函数 。then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。

function timeout(ms){
return new Promise((resolve,reject) => {
    setTimeout(resolve,ms,'done');
})
}
timeout(100).then((value) => {
    console.log(value);
})

上面的代码中,timeout方法返回一个Promise实例 ,表示一段时间以后才会发生的结果。过了指定的时间(ms)以后,Promise实例的状态变为Resolved,就会触发then方法绑定的回调函数。

Promise新建后就会立即执行。

then方法指定的回调函数将在当前脚本所有同步任务执行完成后才会执行

let promise = new Promise(function(resolve,reject){
    console.log('Promise');
    resolve();
})
promise.then(function(){
    console.log('Resolved');
})
console.log('Hi!');
//Promise
//Hi!
//Resolved

不懂这个例子的建议了解一下同步任务,异步任务以及宏任务微任务等 传送门

eg1:异步加载图片

使用Promise包装了一个图片加载的异步操作,如果加载成功,调用resolve方法,否则调用reject方法并传入错误参数。

function loadImgeAsync(url){
	return new Promise(function(resolve,reject){
	var image= new Imge();
     //加载成功执行
     image.onload = function(){
         resolve(image);
	}
    //加载失败执行
     image.onerror = function(){
         reject(new Error('Could not load image at ' + url));
     }
     image.url = url;
    })
}

如果调用resolve函数和reject函数时带有参数,那么这些参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个Promise实例。

var p1 = new Promise(function (resolve,reject){
    //...
})
var p2 = new Promise(function (resolve,reject){
    //...
    resolve(p1);
})

p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作

此时p1的状态会传递给p2,也就是p1的状态会决定p2的状态,如果p1的状态是Pending(进行中),那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是Resolved或Rejected,那么p2的回调函数将会立即执行。

var p1 = new Promise(function(resolve,reeject){
    setTimeout(() => reject(new Error('fail')),3000);
})
var p2 = new Promise(function (resolve,reject){
    setTimeout( () => resolve(p2) , 1000);
})
p2
	.then(result => console.log(result));
	.catch(error = > console.log(error));

上面的代码中,p1是一个Promise,3s后变成rejected,p2的状态在1s之后改变,resolve方法返回p1,。由于返回的是另一个Promise,所以p2状态无效,p1决定p2,再过2s,p1变成rejected,触发了catch绑定的回调函数。

2.Promise.prototype.then()

then方法是定义在原型对象的Promise.prototype上的,它的作用是为Promise实例添加状态改变时的回调函数。第一个参数是Resolved状态的回调函数,第二个参数是Reject状态的回调函数。

then方法返回的是一个新的Promise对象,该对象状态由回调函数的执行结果决定

  • 如果回调函数返回的结果是 非Promise类型的属性 状态为成功 对象成功的值为回调函数返回值(无return返回的就是undefined)

  • 如果回调函数返回的结果是 Promise对象 内部返回的Promise状态决定then方法返回的Promise状态 内部promise成功的值为新Promise成功的值(或失败)

  • 抛出错误 状态为失败 错误的值为throw的值(throw操作符用于在任何时候抛出自定义错误,值的类型不限。)

不清楚then,catch返回值看看这篇博客,传送门

const p = new Promise((resolve,reject) => {
            setTimeout(()=>{
                resolve('用户数据');
            } , 0)
        })
        const result = p.then(value => {
            console.log(value);
            return '123'
        } , reason => {
            console.error(reason);
        })
        console.log(result);

可以采用链式写法。(来解决回调地狱问题)

getJSON("/posts.json").then(function (json){
    return json.post;
}).then(function(post){
    //...
})

上面的代码使用then方法依次指定了俩个回调函数。第一个回调函数完成以后,会将返回结果作为参数传入第二个回调函数。

采用链式的then可以指定一组按照次序调用的回调函数。前一个回调函数有可能返回的还是一个Promise对象,而后一个回调函数就会等待该Promise对象的状态发生变化,再被调用。

3.Promise.prototype.catch()

.then(null , rejection)的别名,(为then方法的语法糖,返回值的情况与then相同),用于指定发生错误时的回调函数。

getJSON('/post.json').then(function(posts){
    //...
}).catch(function(error){
    //处理getJSON和前一个回调函数运行时发生的错误
    console.log('发生错误!',error);
})

getJSON方法返回一个Promise对象,若该对象状态变为Resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态会变为Rejected,然后调用catch方法指定的回调函数处理这个错误。另外,then方法指定的回调函数如果在运行中抛出错误,也会被catch捕获。

4.Promise.all()

将多个Promise实例包装成一个新的Promise实例。接收一个数组(必须具有Iterator接口,且每个返回的成员都是Promise实例)

var p = Promise.all([p1 , p2 , p3]);

p的状态由p1,p2,p3决定,分成俩种情况:

  1. p1,p2,p3的状态都变成fulfilled,p的状态才会变成fulfilled。此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数。
  2. 只要p1,p2,p3中有一个被Rejected,p的状态就变成Rejected,此时第一个被Rejected的实例的返回值会传递给p的回调函数
    以下是俩个关于then,catch返回值以及Promise.all()方法的例子,注意俩个例子的差异(该例子来自阮一峰的ES6这本书)
const p1 = new Promise((resolve,reject) => {
    resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve,reject) => {
    throw new Error('出错啦!')
})
.then(result => result)
.catch(e => e);

Promise.all([p1 , p2])
.then(result => console.log(result))
.catch(e => console.log(e));

//["hello",Error:出错啦!]
//p1,p2的状态如何?值如何?为什么输入内容是这样?
  1. p1:Promise对象状态改变为resolved 触发then方法绑定的回调函数 传入参数为’hello’;then方法返回一个新的Promise实例 由于回调函数返回值为字符串(非Promise对象) 所以新Promise对象的状态为resolved,值为’hello’,该新Promise对象被赋值给p1
  2. p2:Promise对象状态改变为reject 抛出的错误会被catch方法捕获 参数为error实例 ;catch 方法是then方法的语法糖 由于回调函数返回值为error对象(非Promise对象) 所以返回的Promise对象的状态为resolved 值为error实例 将catch返回的Promise对象赋值给p2
  3. Promise.all()p1 p2的状态都是resolved 包装生成的新Promise实例状态为resolved 触发then方法绑定的回调函数 传入的参数为返回值组成的参数数组,于是打印[“hello”,Error:出错啦!]
const p1 = new Promise((resolve,reject) => {
    resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve,reject) => {
    throw new Error('出错啦!')
})
.then(result => result)

Promise.all([p1 , p2])
.then(result => console.log(result))
.catch(e => console.log(e));
//Error:出错啦!
  1. p1:与上面一样
  2. p2:Promise对象状态改变为reject 由于没有用catch绑定回调函数,所以抛出的错误没有被捕获 Promise实例状态变为rejected
  3. Promise.all()p2的状态为rejected 会触发catch绑定的回调函数 传入的参数为p2的返回值(error实例),于是打印 Error:出错啦!

5.Promise.race()

同样是将多个Promise实例包装成一个新的Promise实例

var p = Promise.race([p1 , p2 , p3]);

只要p1,p2,p3中有一个实例率先改变状态,p的状态就跟着改变,率先改变的Promise实例的返回值就传递给p的回调函数。

6.Promise.resolve()

将现有对象转为Promise对象,(若Promsie.all()和Promise.race()的参数数组中的元素不是Promsie实例时,会通过该方法将其转为Promise实例)

  • 参数是一个Promise实例 不做任何修改 原封不动的返回
  • 参数是一个thenable对象(具有then方法的对象),Promise.resolve()会将这个对象转为Promise对象然后立即执行thenable对象的then方法
let thenable = {
    then: function(resolve,reject){
        resolve(42);
    }
};
let p1 = Promise.resolve(thenable);
p1.then(function(value){
    cosnole.log(value);
})
//控制台输出 42

上面的代码中,thenable传入Promise.resolve() 立即执行其then方法 该对象状态变为resolved 进而执行then方法指定的回调函数 输出42

  • 参数不是具有then方法的对象或根本不是对象 如果参数是一个原始值或者是一个不具有then方法的对象,那么该方法返回一个新的promise对象,状态为resolved,值为传入的参数
  • 不传入参数 直接返回一个Resolved状态的对象

立即resolve的Promise对象在本轮"事件循环"结束时。举例:

setTimeout(function(){
    cosnole.log(1);
}, 0 );

Promise.resolve().then(function () {
    console.log(2);
})

console.log(3);
//321
  • 上面的代码,setTimeout属于异步宏任务,进入宏任务EventQueue
  • then属于异步微任务,进入微任务EventQueue
  • console.log()是同步任务,直接执行,输出3
  • 再去微任务队列,发现有一个then,拿出来执行,输出2,本轮时间循环结束
  • 接着进入下一个时间循环,先查看宏任务队列,发现有个setTimeout,拿出来执行,输出1
  • 俩个队列都空了,任务执行结束,所以最后打印的顺序是321

7.Promise.reject()

该方法会返回一个新的Promise实例,状态为Rejected。

var p = Promsie.reject('出错了');
//等同于
var p = new Promise((resolve,reject) => reject('出错了'))

注意:Promsie.reject()方法的参数会原封不动的作为 rejetc 的理由变成后续方法的参数 (这与Promise.resolve()不同)

const thenable = {
    then:function(resolve,reject){
        reject('出错了');
    }
}
Promise.reject(thenable)
.catch(e => console.log(e === thenable))
//true
//会报错.....

后面catch方法的参数也是thenable对象

8.部署俩个有用的方法

done() 处于回调链的尾端 会捕获到任何可能出现的错误 并向全局抛出

Promise.prototype.done = function(onFulfilled,onRejected){
    this.then(onFulfilled,onRejected).catch(function(reason){
        setTimeout(() => {throw reason} , 0); //抛出错误
})
}

finally() 用于指定不管Promise对象最后不管状态如何都会执行的操作 接收一个普通的回调函数作为参数 不管怎样都必须执行

Promise.prototype.finally = function(callback) {
    let P = this.constructor;
    return this.then(
        value => P.resolve(callback()).then(() => value),
        reason => P.resolve(callback()).then(() => {throw reason}),
    )
}

9.Promise.try()

让同步函数同步执行,异步函数异步执行,并且让它们都有相同的API

  • 使用async函数

    const f = () => console.log('now');
    (
        async () => (
            console.log('next')
        )
    )();
    // now
    // next
    
    (
        async () => ()
    )()
    .then(...)
    .catch(...)
    
  • 使用new Promise 使用立即执行函数来执行Promise 同步函数就会同步执行

  • const f = () => console.log('now');
    (
        () => new Promise(
            resolve => resolve( f() )
        )
    )();
    console.log('next');
    //now
    //next
    

关于Promise.try()的提案

Promise.try(f);
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值