Promise基础用法

什么是Promise?

  • Promise是用来处理异步的;
  • Promise就是承诺,对未来的承诺;
  • 所谓的Promise(承诺),里面保存着未来才会结束的事件的结果;
  • Promise是异步编程的一种解决方案; 比传统的解决方案(回调函数和事件)更合理更强大;
  • Promise一个对象,从它可以获取异步操作的消息;
  • Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了

两个特点

  • 对象的状态不受外界影响, Promise对象代表一个异步操作;
3种状态
1 pending 	等待态
2 fulfilled 已成功
3 rejected  已失败

只有异步操作的结果,可以决定当前是哪一个状态,任何外部的操作都无法改变这个状态
复制代码
  • 状态一旦改变,就不会也不能再变,任何时候都可以得到这个结果; Promise的状态,只能从pending变为fulfilled或rejected, fulfilled和rejected不能相互转换;

诞生的时代背景

在JavaScript的世界中,所有代码都是单线程执行的;由于这个"缺陷",以至于JavaScript的所有网络操作、浏览器事件等都必须异步执行; 异步意味着在未来的某个时刻得到结果; 在promise之前,我们写的ajax应用(前一个函数的执行结果作为下一个函数的参数),就面临着大量的回调函数的窘境,回调函数的多层嵌套成为我们绕不开的噩梦;

为解决回调而生

  • Promise对象,可以将异步操作以同步的流程表达出来,避免了层层嵌套的回调函数;
  • Promise对象提供统一的接口,使得控制异步更加容易;

不是最perfect, 因为它还是基于回调

  • 无法取消Promise,一旦新建就会立即执行,无法中途取消;
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部;
  • 当处于pending时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
  • 从本质上来说,Promise还是基于回调的

基础用法

一、构造函数与then

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

  • Promise接收一个函数(executor:执行者)作为参数,该函数有两个参数:

    1 resolve(Function) -> 将Promise对象的状态从"未完成pending"变为"成功resolved",在异步操作成时调用,并将异步操作的结果,作为参数传递出去(供then方法的成功回调接收)

    2 reject(Function) -> 将Promise对象的状态从"未完成pending"变为"rejected",在异步失败时调用,并将异步操作的错误或失败的原因,作为参数传递出去(供then方法的失败回调接收)

const promise = new Promise(function(resolve, reject){

	if(/*异步操作成*/){
		resolve(value); // 成功时调用它会将Promise的状态从"pending"变为"resolved"
	}else{
		reject(err);    // 失败时调用它会将Promise的状态从"pending"变为"rejected"
	}
});

复制代码
  • Promise实例生成后,可以用then方法分别指定resolved(参数是成功的结果)状态和rejected(参数是失败的原因)状态的回调函数; 即then方法接收两个回调函数作为参数:

    1 第一个函数,是Promise对象的状态变为resolved时调用(接收成功的结果作为参数)

    2 第二个函数,是Promise对象的状态变为rejected时调用可选

promise.then((value)=>{
	// value是成功的结果
}, (err)=>{
	// 失败的原因
});

// 示例: demo/1.创建promise实例.js

复制代码
  • Promise的状态发生改变时就会触发then方法对应的不同状态的函数参数

二、Promise新建后会立即执行

  • Promise是同步的,promise.then方法是异步的
const promise = new Promise((resolve, reject)=>{
	
	console.log('Promise');
	resolve();
});

promise.then((value)=>{
    console.log('Success')
});

console.log('Hi!');
// => Promie  Hi! Success
// 上面代码,Promise新建后会立即执行,所以会先输出'Promise', 然后我们为then方法指定了回调函数,因为then方法是异步的,所以它会等待同步代码都执行完之后才执行,所以接下来输出的是'Hi!',当主栈中的同步代码执行完毕之后, 开始执行异步任务(then是个微任务), 因此promise状态改变后成功的回调输出'Success'

// 示例: demo/2.promise创建会立即执行

复制代码
// 异步加载图片示例
function loadImageAsync(url){
    return new Promise((resolve, reject) => {
        let image = new Image();

        image.onload = function(){
            resolve(image);  // 如果图片加载成则调用该方法, 将promise的状态改为resolved
        };

        image.onerror = function(){
            reject('创建失败'); // 如果图片加载失败, 则调用该方法, 将promise的状态改为rejected
        };

        image.src = url;
    });
}

loadImageAsync('./timg2.jpg').then((value)=>{
    // 如果状态变为成功态,则将图片添加到页面
    console.log(value);
    document.querySelector('body').appendChild(value);
}, (err)=>{
    // 状态变为失败态,则将提示错误
    console.log('Error:', err);
});


// 示例: demo/3.异步加载图片

复制代码
// Promise对象实现Ajax示例
let getJSON = function(){
    return new Promise((resolve, reject)=>{
        $.getJSON('./test.json').then((value)=>{
            console.log(value);
            resolve(value)
        }, (err)=>{
            // console.log('失败回调:',err)
            reject(err);
        });  
    });

};
getJSON().then((data)=>{
    console.log(data)
},(err)=>{
    console.log('My-Error:', err);
});

// 如果调用resolve函数和reject函数时带有参数,那么它们的参数被被传递给回调函数(then的成功回调和失败回调)


复制代码
  • Promise的回调函数中抛出错误或异常,则直接调用then的失败态(reject)回调函数
new Promise((resolve,reject)=>{
    throw new Error('错误');
}).then((data)=>{
    console.log(data)
},(err) => {
    console.log('Err ', err);  //Err  Error: 错误....
    return err;
})

复制代码

三、resolve函数返回Promise实例

resolve函数的参数除了正常的值以外,还能是另一个Promise实例

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

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

上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作

注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行
复制代码
const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
// Error: fail
//上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数



// 一言以蔽之: "传递的promise都要执行,直到执行后的结果是普通值(非promise)或抛出异常, 再传递到下一个then中, 执行对应的回调"
复制代码

四、调用resolve与reject并不会终结Promise的参数函数(executor)执行

new Promise((resolve,reject)=>{
    resolve('success');
    console.log('Hello')
}).then(data=>{
    console.log(data)
});

// => 'Hello' 'success'
// 一般来说, 调用resolve和reject以后,Promise的使命就完成了,后续的操作应该放到then方法里面,而不应该直接写在resolve和reject的后面; 所以,最好在它们前面加上return语句;
复制代码
new Promise((resolve,reject)=>{
    return resolve('success');
    console.log('Hello')
}).then(data=>{
    console.log(data)
});
// 此时只输出success

复制代码

五、Promise.prototype.then()

  • Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的;
  • then方法的作用是为Promise实例添加状态改变时的回调函数,它的第一个参数是resolve状态的回调函数,第二个参数是rejected状态的回调函数;
  • then方法返回的是一个新的Promise实例(注意不是原来那个Promise实例);
  • 链式写法,即then方法后面再调用另一个then方法

1、 then链式调用, 第一个then回调的返回结果会作为第二个then回调的参数

new Promise((resolve,reject)=>{
    return resolve('success');   	// 将状态改变为成功态
}).then((data)=>{
    console.log(data)				// success
    return '成功' + data;
},(err) => {
    console.log('Err', err);
    return err;
}).then((data) => {
    console.log(data)				// 成功success
}, (err) =>{
    console.log("Fail", err)
});

// 上面代码使用then方法, 依次执行两个then的回调函数. 第一个回调完成后,会将返回结果作为参数,传入到第二个回调函数;
复制代码

2、 then的reject回调函数中返回错误,会继续传递到下一个then的成功(resolve)回调 切记

new Promise((resolve,reject)=>{
    return reject('fail');  // 执行失败回调
}).then((data)=>{
    console.log(data); 		
    return '成功';
},(err) => {
    console.log('Err', err);  // Err fail
    return err;
}).then((data) => {
    console.log('hello',data) // hello fail
}, (err) =>{
    console.log("Fail", err)
});

复制代码

3、then()会多层传递

new Promise((resolve,reject)=>{
    resolve('ABC');
}).then().then().then((data)=>{
    console.log(data)
},(err)=>{
    console.log(err)
});
// 输出 "ABC"
复制代码

六、Promise.prototype.catch()

  • Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数
new Promise((resolve,reject)=>{
    throw new Error('错误');
}).catch((err)=>{
	console.log('Error:', err); // Error: Error: 错误
});

等同于

new Promise((resolve,reject)=>{
    throw new Error('错误');
}).then(null, (err)=>{
	console.log('Error:', err); // Error: Error: 错误
});

复制代码
  • 在Promise的回调函数中直接抛出错误,此时状态变为rejected, 错误会被catch捕获
new Promise((resolve,reject)=>{
    throw new Error('错误');
}).catch((err)=>{
	console.log('Error:', err); // Error: Error: 错误
});

复制代码
  • then方法指定回调函数, 在运行中抛出错误, 也会被catch捕获
new Promise((resolve,reject)=>{
    resolve('ABC');   	// 将状态改变为成功态
}).then((data)=>{
    console.log(test)	// 在then的成功回调中执行,输出一个未定义的变量,此时会抛出错误
},(err) => {
    console.log('Err ', err);
}).catch((err)=>{
    console.log('Catch ', err); // 在这里被捕获 => Catch  ReferenceError: test is not defined ....
});

复制代码
  • 如果Promise的状态已经变成resolved, 再抛出错误是无效的
new Promise((resolve,reject)=>{
    resolve('ABC');
    throw new Error('错误');
}).then((data)=>{
    console.log(data)  // 输出ABC
},(err) => {
    console.log('Err ', err);
}).catch((err)=>{
    console.log('Catch ', err);
});

// 上面代码执行输出了"ABC",说明在状态改变后抛出错误,对结果无影响
// 上面代码中,Promise在resolve语句后面抛出错误,没有被捕获,相当于没有抛出错误;因为Promise的状态一旦改变,就永远保持该状态,不会再变了;
复制代码
  • reject的作用等同于抛出错误
new Promise((resolve,reject)=>{
    reject('ABC');
}).catch((err)=>{
    console.log('Catch ', err);
});
// 输出: "Catch  ABC"

等同于
new Promise((resolve,reject)=>{
    try{
        console.log('a')
        throw new Error('test');
    }catch(e){
        console.log('b')
        reject(e)
    }
}).catch((err)=>{
    console.log('Catch ', err);
});
// => a b Catch  Error: test
复制代码

Promise.all()

  • Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例
  • Promise.all方法接受一个数组作为参数(数组中的每一项都是一个promise实例),只有所有的实例状态都变为fulfilled, promise实例的状态才会变成fulfilled, 此时数组中promise实例的返回值会组成一个数组,传递给该promise实例的回调函数, 返回值在数组中的顺序与promise数组中promise实例的顺序一致
let p1 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(100);
    }, 2000);
});

let p2 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve('Hello');
    }, 3000);
});

let p3 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(true);
    }, 2000);
});

Promise.all([p1, p2, p3]).then((data)=>{
    console.log(data);  // 等数组中的promise实例都执行完成后,才将状态改变为fulfilled => [ 100, 'Hello', true ]
}, (err)=>{
    console.log(err)
});

// => [ 100, 'Hello', true ]
复制代码
  • 如果其中有一个promise实例抛出错误,就会走到Promise.all().catch()方法
let p1 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(100);
    }, 2000);
});

let p2 = new Promise((resolve,reject)=>{
    throw Error('错误啦');
});


let p3 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(true);
    }, 2000);
});

Promise.all([p1, p2, p3]).then((data)=>{
    console.log(data)
}).catch(err=>{
    console.log('Promise.all Error:', err)
});

// => Promise.all Error: Error: 错误啦....
// 在p2执行时抛出了异常,那么整个Promise.all()执行状态被改变为rejected, 错误被catch捕获

复制代码
  • 如果作为参数的Promise实例,自己定义了catch方法, 那么一旦被rejected,并不会触发Promise.all()的catch方法
let p1 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(100);
    }, 2000);
});

let p2 = new Promise((resolve,reject)=>{
    throw Error('错误啦');
}).catch(err=>{
    console.log('p2.Error', err);		// p2.Error Error: 错误啦
});

Promise.all([p1, p2]).then((data)=>{
    console.log(data);					// [ 100, undefined ]
}).catch(err=>{
    console.log('Promise.all Error:', err)
});

// 上例中,p1在2秒后状态改变为resolve; p2执行时抛出错误,此时p2的状态改变为rejected, 因为它有自己的catch,在执行完catch方法后,状态也变成了resolve, 因此会调用then的指定的回调, 而不会调用catch方法指定的回调函数; 所以,最终打印出: [ 100, undefined ]
复制代码

Promise.race()

  • Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例
  • 同上面all()方法相比,只要有一个实例率先改变状态,整个promise实例的状态就跟随改变; 与此同时,第一个改变的promise实例(数组中的)的值,被传递给Promise的回调函数
let p1 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(100);
    }, 2000);
});

let p3 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(true);
    }, 2500);
});

Promise.race([p1,  p3]).then((data)=>{
    console.log(data)		// 100
}).catch(err=>{
    console.log('Promise.all Error:', err)
});
复制代码
  • 如果promise数组中有一个普通值,会立马返回
let p1 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(100);
    }, 2000);
});

let p3 = new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(true);
    }, 2500);
});

Promise.race([p1,  p3, 10086]).then((data)=>{
    console.log(data)	// 10086
}).catch(err=>{
    console.log('Promise.all Error:', err)
});

// => 10086; 因为我们最终要使用的是普通值或普通对象,而非promsie,所有的实例最终的目的都是要返回普通值, 因此遇到普通值会立马返回
复制代码

Promise.resolve()

  • Promise.resolve()可用于将现有对象转换为Promise对象

  • Promise.resolve('foo') 等价于new Promise(resolve=>resolve('foo'))

  • Promise.resolve方法的参数分成四种情况: 1、参数是一个 Promise 实例

    • 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例

    2、参数是一个thenable对象(thenable对象指的是具有then方法的对象)

    • Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法
     let thenable = {
       then: function(resolve, reject) {
         resolve(42);
       }
     };
     
     let p1 = Promise.resolve(thenable);
     p1.then(function(value) {
       console.log(value);  // 42
     });
    
     // thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42	
    
    复制代码

    3、参数不是具有then方法的对象,或根本就不是对象

    • 如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise对象,状态为resolved
    const p = Promise.resolve('Hello');
    p.then(function (s){
      console.log(s)
    });
    // Hello
    // 上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数
    复制代码

    4、不带有任何参数

    • Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的Promise对象,所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法

Promise.reject()

  • Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了
//上面代码生成一个Promise对象的实例p,状态为rejected,回调函数会立即执行
复制代码
  • Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数.这一点与Promise.resolve方法不一致
const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true
// 上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的"出错了"这个字符串,而是thenable对象
复制代码

参考文档

ECMAScript 6 入门 - Promise对象

转载于:https://juejin.im/post/5c9ba2f751882531f12dd0ba

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值