Promise及其应用

一、概念

1.Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件,更合理和强大

2.Promise对象的特点:

  • 对象的状态不受外界影响(三种状态:pending(进行中)、 fullfilled(已成功)、rejected(已失败))
  • 一旦状态发生改变,就不会再变,任何时候都可以得到这个结果(pending -> fulfilled, pending ->rejected, resolved定型)

3.Promise的缺点:

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

4.列子和补充

console.log("a");
setTimeout(() => console.log("b"));
let p = new Promise((resolve, reject) => {
    resolve();
    console.log("c");
}).then(() => {
  console.log("d");
});
console.log("e");

输出顺序:a->c->e->d->b

1.JS有一个宏任务队列,有一个微任务队列,执行循环,当执行栈为空,先清空微任务,在执行宏任务.

2.then仅仅是把第一个注册到onfulfilled,第二个注册到rejected.

3.setTimeout第一个函数是当定时时间到执行的回调函数.

4.一个Promise执行resolve时,状态改变为fulfilled,值改变为resolve传入的值.

二、原理

原理上Promise 也还是使用回调函数,只不过是把回调封装在了内部,使用上一直通过 then 方法的链式调用,使得多层的回调嵌套看起来变成了同一层的,书写上以及理解上会更直观和简洁一些。

 new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({ test: 1 })
            resolve({ test: 2 })
            reject({ test: 2 })
        }, 1000)
    }).then((data) => {
        console.log('result1', data)
    },(data1)=>{
        console.log('result2',data1)
    }).then((data) => {
        console.log('result3', data)
    })

输出结果:

result1 { test: 1 }

result3 undefined

显然这里输出了不同的 data。由此可以看出几点:

  1. 可进行链式调用,且每次 then 返回了新的 Promise(2次打印结果不一致,如果是同一个实例,打印结果应该一致。
  2. 只输出第一次 resolve 的内容,reject 的内容没有输出,即 Promise 是有状态且状态只可以由pending -> fulfilled或 pending-> rejected,是不可逆的。
  3. then 中返回了新的 Promise,但是then中注册的回调仍然是属于上一个 Promise 的。

三、用法以及应用场景

异步编程

通常来说,程序都是顺序执行的,同一时刻只会发生一件事。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户的角度来说,整个程序才算运行完毕。

  1. 异步执行的运行机制

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

  只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。

     2.异步编程与Promise的关系:

Promise对象可以理解为一次执行的异步操作,使用promise对象之后可以使用一种链式调用的方式来组织代码;让代码更加的直观。 也就是说,有了Promise对象,就可以将异步操作以同步的操作的流程表达出来,避免了层层嵌套的回调函数。 总结一下就是可以将原先不可控的回调通过promise转为更加可控更清晰的方式表达,更加高效,更便于维护。

Promise的作用与用法

基本用法

Promise使用实例:

function asyncDo(){
    return new Promise(function(resolve,reject){
        // some codes
        if(/*异步操作成功*/){
            resolve(value); //异步操作成功,将value传递出去
        }else{
            reject(erroe);  //异步操作失败,报出错误,将error传递出去
        }
    });
}

链式调用

let Promise1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          let t = true;
          if (t) {
            resolve("success");
          } else {
            reject("failed");
          }
        }, 1000);
      });
      Promise1.then((r) => {
        console.log(r);
        return r + "1";
      })
        .then((r) => {
          console.log(r);
          return r + "2";
        })
        .then((r) => {
          console.log(r);
        });
//success
//success1
//success12

错误传递


      new Promise((resolve, reject) => {
        console.log("in Promise");
        resolve();
      })
        .then(() => {
          throw new Error("then Error");
          console.log("then");
        })
        .then((r) => {
          console.log("then continue");
        })
        .catch((err) => {
          console.log("catch error" + err.message);
        });
//in Promise
//catch errorthen Error
    

利用catch来终止Promise链,避免链条中的rejection抛出错误到全局

Promise在实际环境下的应用

promise.all方法

var p1 = new Promise(function (resolve) {
   setTimeout(function () {
       resolve("第一个promise");
   }, 3000);
});
 
var p2 = new Promise(function (resolve) {
   setTimeout(function () {
       resolve("第二个promise");
   }, 1000);
});
 
Promise.all([p1, p2]).then(function (result) {
   console.log(result); // ["第一个promise", "第二个promise"]
});

上面的代码中,all接收一个数组作为参数,p1,p2是并行执行的,等两个都执行完了,才会进入到then,all会把所有的结果放到一个数组中返回,所以我们打印出我们的结果为一个数组。值得注意的是,虽然p2的执行顺序比p1快,但是all会按照参数里面的数组顺序来返回结果

应用场景1:多个请求结果合并在一起

具体描述:一个页面,有多个请求,我们需求所有的请求都返回数据后再一起处理渲染

应用场景2:合并请求结果并处理错误

描述:我们需求单独处理一个请求的数据渲染和错误处理逻辑,有多个请求,我们就需要在多个地方写

应用场景3:验证多个请求结果是否都是满足条件

描述:在一个微信小程序项目中,做一个表单的输入内容安全验证,调用的是云函数写的方法,表单有多7个字段需要验证,都是调用的一个 内容安全校验接口,全部验证通过则 可以 进行正常的提交

promise.race方法

race的意思为赛跑,因此,promise.race也是传入一个数组,但是与promise.all不同的是,race只返回跑的快的值,也就是说result返回比较快执行的那个。

var p1 = new Promise(function (resolve) {
   setTimeout(function () {
       console.log(1);
       resolve("第一个promise");
   }, 3000);
});
 
var p2 = new Promise(function (resolve) {
   setTimeout(function () {
       console.log(2);
       resolve("第二个promise");
   }, 1000);
});
 
Promise.race([p1, p2]).then(function (result) {
   console.log(result); 
});
// 2
// 第二个promise
// 1

应用场景1:图片请求超时

应用场景2:请求超时提示

描述:有些时候,我们前一秒刷着新闻,下一秒进入电梯后,手机页面上就会提示你 “网络不佳”

Promise.prototype.then

p.then(onFulfilled[, onRejected]);

p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});

应用场景1:下个请求依赖上个请求的结果

描述:类似微信小程序的登录,首先需要 执行微信小程序的 登录 wx.login 返回了code,然后调用后端写的登录接口,传入 code ,然后返回 token ,然后每次的请求都必须携带 token,即下一次的请求依赖上一次请求返回的数据

应用场景2:中间件功能使用

描述:接口返回的数据量比较大,在一个then 里面处理 显得臃肿,多个渲染数据分别给个then,让其各司其职

async/await的作用与用法

async/await 是ES2017(ES8)提出的基于Promise的解决异步的最终方案。

async

async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。

await

await也是一个修饰符,只能放在async定义的函数内。可以理解为等待。

await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;

如果不是Promise对象:把这个非promise的东西当做await表达式的结果。

async/await 的正确用法

// 使用async/await获取成功的结果
// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('获取成功')
        },3000)
    })
}

async function test(){
    let a = await getSomeThing();
    console.log(a)
}
test(); // 3秒后输出:获取成功
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值