聊聊Promise和Async

因为前端都是自己研究,实践不多,所以一直以来对promise和async的认识都停留在表层,最近对这两个概念进行了深入的研究,现在我们就来聊聊Promise和Async。

一.promise

先来说说我之前对promise的理解

promise是一个对象,但并不是一个普通的对象,整个对象中封装了一个耗时的任务,这个对象有三种状态pending(进行中),resolved(任务完成),rejected(任务失败),resolved和rejected都可以传入参数,参数会传递给then中的函数。同时在promise对象的后面跟着then(),then中可以放两个回调函数,当promise对象状态变为resolved就调用then中的第一个方法,状态变为rejected就调用第二个方法。

好吧,就上面这些了,具体对于promise如何实现的,promise还有没有其他方法一概不知。。

今天深入学习后总结的几个点:

1. 当向resolved中传入另一个promise对象时

注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是Pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是Resolved或者Rejected,那么p2的回调函数将会立刻执行。

2.Promise.prototype.catch()

catch方法可用可不用,当使用catch时,then中定义的第二个回调函数就要写到catch中,状态就会变为Rejected时,就会调用catch方法指定的回调函数。同时,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

3.Promise.all()

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

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

上面代码中,Promise.all方法接受一个数组作为参数,p1p2p3都是Promise对象的实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例。)

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成Resolvedp的状态才会变成Resolved,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

4.Promise.race()

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。与all方法区别在于只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

5.Promise.resolve()

Promise.resolve方法用于将一个对象转化为promise对象

6.done()

Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。

asyncFunc()
  .then(f1)
  .catch(r1)
  .then(f2)
  .done();

7.finally()

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

下面是一个例子,服务器使用Promise处理请求,然后使用finally方法关掉服务器。

server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);

8.Promise.try()

实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。

Promise.resolve().then(f)

上面的写法有一个缺点,就是如果f是同步函数,那么它会在下一轮事件循环执行。

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

上面代码中,函数f是同步的,但是用 Promise 包装了以后,就变成异步执行了。

二.async

我对于async的理解就是,他是对promise的又一次封装,当有多个promise需要按顺序执行时,我们不需要再promise1

.then(promise2).then(promise3)。。。了,可以写成下面这样:

async function main() {
    await promise1;
    await promise2;
    await promise3;
}

理解起来应该算是十分的简单了,就是按顺序执行这三个promise对象。

async就像一个更大的promise,它也有then和catch方法,它内部可以有一个return语句返回值,返回的是一个promise对象,返回值会变成then的参数。如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject,错误信息会被catch接收。为了避免因为报错停止执行,我们可以将await放在try...catch结构里面,这样接下来的await就会继续执行。

下面来说说async的底层实现

async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。

async function fn(args){
  // ...
}

// 等同于

function fn(args){
  return spawn(function*() {
    // ...
  });
}

所有的async函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。

下面给出spawn函数的实现,基本就是前文自动执行器的翻版。

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    var gen = genF();
    function step(nextF) {
      try {
        var next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

 

生成器实现机制——协程

可能你会比较好奇,生成器究竟是如何让函数暂停, 又会如何恢复的呢?接下来我们就来对其中的执行机制——协程一探究竟。

什么是协程?

协程是一种比线程更加轻量级的存在,协程处在线程的环境中,一个线程可以存在多个协程,可以将协程理解为线程中的一个个任务。不像进程和线程,协程并不受操作系统的管理,而是被具体的应用程序代码所控制。

协程的运作过程

那你可能要问了,JS 不是单线程执行的吗,开这么多协程难道可以一起执行吗?

答案是:并不能。一个线程一次只能执行一个协程。比如当前执行 A 协程,另外还有一个 B 协程,如果想要执行 B 的任务,就必须在 A 协程中将JS 线程的控制权转交给 B协程,那么现在 B 执行,A 就相当于处于暂停的状态。

举个具体的例子:

function* A() {
  console.log("我是A");
  yield B(); // A停住,在这里转交线程执行权给B
  console.log("结束了");
}
function B() {
  console.log("我是B");
  return 100;// 返回,并且将线程执行权还给A
}
let gen = A();
gen.next();
gen.next();

// 我是A
// 我是B
// 结束了
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值