聊聊Promise和Async

xiaoxiao2025-10-08  23

因为前端都是自己研究,实践不多,所以一直以来对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方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例。)

p的状态由p1、p2、p3决定,分成两种情况。

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

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

4.Promise.race()

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。与all方法区别在于只要p1、p2、p3之中有一个实例率先改变状态,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 // 结束了  
转载请注明原文地址: https://www.6miu.com/read-5037539.html

最新回复(0)