我理解的promise

什么是Promise?

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一,最早由社区提出并实现,典型的一些库有Q,when, bluebird等;它的出现是为了更好地解决JavaScript中异步编程的问题,传统的异步编程最大的特点就是地狱般的回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Promise则可以让我们通过链式调用的方法去解决回调嵌套的问题,使我们的代码更容易理解和维护,而且Promise还增加了许多有用的特性,让我们处理异步编程得心应手。

Promise,就是一个对象,用来传递异步操作的消息。代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。

常见异步编程方案

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象

Promise出现的原因

在Promise出现以前,我们处理一个异步网络请求通常使用回调函数解决方案,对于回调函数,我们用ajax获取数据时 都是以回调函数方式获取的数据,我们来看个例子:

// request 表示 一个异步网络调用,result1 表示网络请求的响应。
request1(function(result1){
    处理result1
})

上述代码看起来好像没有什么问题,但是如果我们需要根据第一请求的结果去处理第二个网络请求,那么代码会是下面这个样子:

request1(function(result1){
    request2(function(result2){
        处理result2
    })
})

上面代码看起好像还好,但是有可能会出现第二次请求的结果,需要在第三次异步请求时使用,就是多个异步请求之间是依赖关系,那么我们的代码就会变成下面这个样子,即形成回调地狱:

request1(function(result1){
 request2(function(result2){
  request3(function(result3){
   request4(function(result4){
    request5(function(result5){
        ......等等
     })
    })
   })
 })
})

上述代码不好的是我们基本上还要对每次请求的结果进行处理,代码会更加臃肿,如果我们在一个团队中,那么代码review以及后续维护将会是一个很困难的过程。

回调地狱带来的问题
  • 代码臃肿。
  • 可读性差。
  • 耦合度过高,可维护性差。
  • 代码复用性差。
  • 容易产生bug。
  • 只能在回调里处理异常。

面对上述问题,就有人思考了,能不能用一种更加友好的代码去解决上述异步嵌套的问题。于是Promise规范诞生了,并且在业界有了很多实现来解决回调地狱的痛点。

Promise处理多个相互关联的异步请求

Promise可以更直观的方式来解决"回调地狱"
我们来看个例子:

const promise = url => { 
    return new Promise((resolve, reject) => {
        $.get(url, res => {
            resolve(res)
        });
    })
};
// 请求data1
promise(url).then(res1 => {
    return promise(res1.url);   
}).then(res2 => {
    return promise(res2.url);
}).then(res3 => {
    console.log(res3);
}).catch(err => throw new Error(err));
可以总结为如下代码形式:
new Promise(request1)
    .then(request2(result1))
    .then(request3(result2))
    .then(request4(result3))
    .then(request5(result4))
    .catch(处理异常(异常信息))

我们可以比较下这种写法和上面的回调式的写法。会发现,Promise 的写法更为直观,简洁明了,并且能够在外层捕获异步函数的异常信息。

如何使用promise

ES6给我们提供了一个原生的构造函数Promise,Promise是一个构造函数,我们可以看下这个构造函数:

console.logtypeof Promise();
// "function" 可以看出这是一个构造函数
console.log(Promise);
// function Promise() { [native code] } // ES6的原生支持

new Promise返回一个promise对象接收一个excutor执行函数作为参数, excutor有两个函数类型形参resolve reject。

let promise = new Promise((resolve, reject) => {
       // 异步请求处理
       // 异步请求结束后、调用resolve或reject
});

我们对上述代码解说一下:

  • Promise是一个构造函数,所以使用new操作符来创建promise。
  • 构造函数Promise的参数是一个函数(func),这个函数有两个参数resolve和reject,分别是两个函数,作用就是将promise的状态从pending转换为resolved或者从pending转换为rejected。
  • 创建后的promise有一些方法,then和catch。当然我们也可人为的在Promise函数上添加一些满足自己需求的方法,方便promise对象使用。

promise相当于一个状态机, 有三种状态:

  • pending
  • fulfilled
  • rejected
    promise 对象初始化时状态为pending,当调用resolve(成功),会由pending => fulfilled, 当调用reject(失败),会由pending => rejected。 状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
通俗一点来讲就是:

Promise函数体的内部包裹着一个异步的请求或者操作或者函数;然后我们可以在这个异步的操作完成的时候使用resolve函数将我们获得的结果传递出去,或者使用reject函数将错误的消息传递出去。

注意:promsie状态只能由pending => fulfilled/rejected, 一旦修改就不能再变。
promise对象方法
  • then方法注册resolve(成功)/reject(失败)的回调函数
// onFulfilled接收promise成功的值
// onRejected接收promise失败的原因
promise.then(onFulfilled, onRejected);
注意:then方法是异步执行的
  • resolve(成功) onFulfilled会被调用
let promise = new Promise((resolve, reject) => {
  resolve('fulfilled'); // 由pending => fulfilled
});
promise.then(result => { // onFulfilled
   console.log(result); // 'fulfilled' 
}, reason => { // onRejected不会被调用
   
})
  • reject(失败) onRejected会被调用
let promise = new Promise((resolve, reject) => {
  reject('rejected'); // 由pending => rejected
});
promise.then(result => { // onFulfilled 不会被调用
}, (err) => { // onRejected 
   console.log('rejected',err); // 'rejected'
})
  • promise.catch
    在链式写法中可以捕获上述代码中发送的异常
promise.catch(onRejected)
相当于
promise.then(null, onRrejected);
// 注意onRejected不能捕获当前onFulfilled中的异常
promise.then(onFulfilled, onRrejected); 
// 可以写成:
promise.then(onFulfilled)
       .catch(onRrejected); 
  • promise chain
    promise.then方法每次调用都返回一个新的promise对象所以可以写成链式
    调用形式
function fn1() {console.log("Task 1");}
function fn2() {console.log("Task 2");}
function onRejected(error) {console.log("Catch Error: 1 or 2", error);}
var promise = Promise.resolve();
promise
  .then(fn1())
  .then(fn2())
  .catch(onRejected()) // 捕获异常
总结:
  • 我们可以看到,then接受两个函数作为参数,第一个函数是用来处理resolve的结果,第二个是可选的,用来处理reject的结果。即我们在创建Promise对象的时候,通过函数resolve传递出去的结果可以被promise的第一个then方法中的第一个函数捕获然后作为它的参数。通过函数reject传递出去的结果可以被promise的第一个then方法中的第二个函数捕获然后作为它的参数。总结一句话就是(then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调)
  • 每一个then方法中都可以再次新创建一个Promise对象,然后返还给下一个then方法处理。
  • resolve找then里的成功回调,reject找then里失败的回调。
Promise的静态方法
  • Promise.resolve返回一个fulfilled状态的promise对象
Promise.resolve('node').then(function(res){
   console.log(res); // node
});
Promise.resolve('node'); // node
// 相当于
let promise = new Promise(resolve => {
  resolve('node'); // node
});
  • Promise.reject返回一个rejected状态的promise对象
Promise.reject(‘失败’);
new Promise((resolve, reject) => {
   reject(‘失败’);
});
  • Promise.all接收一个promise对象数组为参数
    只有全部为resolve才会调用,通常会用来处理多个并行异步操作
    谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都返回Promise对象。
    有了all,就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很爽?有一个场景是很适合用这个,比如一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。
let p1 = new Promise((resolve, reject) => {
   resolve(1);
});
let p2 = new Promise((resolve, reject) => {
   resolve(2);
});
let p3 = new Promise((resolve, reject) => {
   resolve(3);
});
Promise.all([p1, p2, p3]).then(data => { 
   console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
}, err => {
   console.log(err);
});
  • Promise.race 接收一个promise对象数组为参数
    Promise.race 只要有一个promise对象进入FulFilled或者Rejected 状态的话,就会继续进行后面的处理。
    谁跑的快,以谁为准执行回调
function timerPromisefy(delay) {
   return new Promise(function (resolve, reject) {
       setTimeout(function () {
           resolve(delay);
       }, delay);
   });
}
var startDate = Date.now();
Promise.race([
   timerPromisefy(10),
   timerPromisefy(20),
   timerPromisefy(30)
]).then(function (values) {
   console.log(values); // 10
});

Promise特点

  • Promise的立即执行性
const promise = new Promise(function(resolve, reject){
 console.log("create a promise");
 resolve("success");
});
console.log("after new Promise");
promise.then(function(res){
 console.log(res);
});
输出: // "create a promise"
// "after new Promise"
// "success"

Promise对象表示未来某个将要发生的事件,但在创建(new)Promise时,作为Promise参数传入的函数是会被立即执行的,只是其中回调执行的代码是异步代码。

  • Promise 状态的不可逆性
const promise1 = new Promise(function(resolve, reject){
  resolve("success1");
  resolve("success2");
});
const promise2 = new Promise(function(resolve, reject){
  resolve("success");
  reject("reject");
});
promise1.then(function(res){
  console.log(res);
});
promise2.then(function(res){
  console.log(res);
});
// 输出结果: "success1" "success"
  • 链式调用
var p = new Promise(function(resolve, reject){
 resolve(1);
});
p.then(function(value){               //1
 console.log(value);
 return value*2;
}).then(function(value){              //2
 console.log(value);
}).then(function(value){              //3
 console.log(value);
 return Promise.resolve('resolve'); 
}).then(function(value){              //4
 console.log(value);
 return Promise.reject('reject');
}).then(function(value){              //5
 console.log('resolve: '+ value);
}, function(err){
 console.log('reject: ' + err);
})
// 输出:
1
2
undefined
"resolve"
"reject: reject"

Promsie 与事件循环

Promise在初始化时,传入的函数是同步执行的,然后注册then回调,注册完之后,继续往下执行同步代码,在这之前,then 中回调不会执行。同步代码块执行完毕后,才会在事件循环中检测是否有可用的 promise 回调,如果有,那么执行,如果没有,继续下一个事件循环。

promise应用场景

  • 回调地狱,代码难以维护, 通常第一个的异步请求的输出是第二个请求的输入这种现象(多个请求互相依赖关系)。
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • promise可以解决异步的问题,本身不能说promise是异步的

promise使用总结

  • 首先初始化一个 Promise 对象,可以通过两种方式创建, 这两种方式都会返回一个 Promise 对象。
    1、new Promise(fn)
    2、Promise.resolve(fn)
  • 然后调用上一步返回的 promise 对象的 then 方法,注册回调函数。
    1、then 中的回调函数可以有一个参数,也可以不带参数。如果 then 中的回调函数依赖上一步的返回结果,那么要带上参数。比如
    new Promise(fn)
    .then(fn1(value){
        //处理value
    })
  • 最后注册catch异常处理函数,处理前面回调中可能抛出的异常。
    通常通过这三个步骤,就能够应对绝大部分的异步处理场景。

最后我们看一些常见的promise例子:

demo1:

const p1 = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})
p1.then(() => {
  console.log(3)
})
console.log(4)

最终输出结果是:1 2 4 3,Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的
demo2:

const p1 = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')
})
p1.then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })

最终输出结果:success1,构造函数中的resolve或reject只有第一次执行有效,多次调用没有任何作用,即promise 状态一旦改变则不能再变。
后续会有一篇文章讲解下如何手写一个promise。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Promise 是 JavaScript 中处理异步编程的一种机制。它是一个代表了未来可能完成或失败的操作的对象。Promise 对象可以用来处理异步操作,比如从服务器获取数据、读取文件等等。 Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。当一个 Promise 对象由 pending 状态转变为 fulfilled 或 rejected 状态时,它被称为“settled”。 使用 Promise 可以更优雅地处理异步操作。通过 Promise,我们可以将异步的操作包装为一个对象,然后通过链式调用的方式来处理返回的结果或错误。这种方式避免了回调地狱(callback hell)的问题,使得代码更加可读、易于维护。 Promise 提供了一些方法来处理异步操作的结果,包括 then、catch 和 finally。then 方法用于处理成功的结果,catch 方法用于处理错误,finally 方法无论成功或失败都会执行。 例如,我们可以通过以下方式创建一个 Promise 对象并使用 then 方法处理异步操作的结果: ```javascript const promise = new Promise((resolve, reject) => { // 异步操作 // 如果操作成功,则调用 resolve(result) // 如果操作失败,则调用 reject(error) }); promise.then((result) => { // 处理成功的结果 }).catch((error) => { // 处理错误 }); ``` 通过使用 Promise,我们可以更加清晰地表达异步操作的顺序和逻辑,提高代码的可读性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值