期约与异步函数

Posted weixin_48419976

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了期约与异步函数相关的知识,希望对你有一定的参考价值。

  • 11.1 异步编程
    异步行为是为了优化因计算量大而时间长的操作。如果在等待其他操作完成的同时,即使运行其他指令,系统也能保持稳定.量大或要等很长时间。你不想为等待某个异步操作而阻塞线
    程执行,那么任何时候都可以使用。
     
  • 同步与异步
    同步行为每条指令都会严格按照它们出现的顺序来执行 
    如果代码要访问一些高延迟的资源,比如向远程服务器发送请求并等待响应,那么就会出现长时间的等待。
    异步操作的例子可以是在定时回调中执行一次简单的数学计算:
    let x = 3;
    setTimeout(() => x = x + 4, 1000);

    这段程序最终与同步代码执行的任务一样,都是把两个数加在一起,但这一次执行线程不知道 x 值何时会改变,因为这取决于回调何时从消息队列出列并执行
  • 11.2 期约promise
    ECMAScript 6 新增的引用类型 Promise ,可以通过 new 操作符来实例化。创建新期约时需要传入执行器(executor)函数作为参数
    let p = new Promise(() => );
    setTimeout(console.log, 0, p); // Promise <pending>

     在把一个期约实例传给 console.log() 时,控制台输出(可能因浏览器不同而略有差异)表明该
    实例处于待定(pending)状态。如前所述,期约是一个有状态的对象,有三种状态
    待定(pending)
     兑现(fulfilled,有时候也称为“解决”,resolved)
     拒绝(rejected)
    期约主要有两大用途。首先是抽象地表示一个异步操作。期约的状态代表期约是否完成。“待定”表示尚未开始或者正在执行中。“兑现”表示已经成功完成,而“拒绝”则表示没有成功完成。

    比如,假设期约要向服务器发送一个 HTTP 请求。请求返回 200~299 范围内的
    状态码就足以让期约的状态变为“兑现”。类似地,如果请求返回的状态码不在 200~299 这个范围内,那么就会把期约状态切换为“拒绝”。
    只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

控制期约状态的转换是通过调用它的两个函数参数实现的。这两个函数参数通常都命名为 resolve() 和 reject()

let p1 = new Promise((resolve, reject) => resolve());
setTimeout(console.log, 0, p1); // Promise <resolved>
let p2 = new Promise((resolve, reject) => reject());
setTimeout(console.log, 0, p2); // Promise <rejected>
// Uncaught error (in promise)
  • 在 ECMAScript 暴露的异步结构中,任何对象都有一个 then() 方法
    把期约逐个地串联起来是一种非常有用的编程模式。之所以可以这样做,是因为每个期约实例的方法( then() 、 catch() 和 finally() )都会返回一个新的期约对象,而这个新期约又有自己的实例方法。这样连缀方法调用就可以构成所谓的“期约连锁”。比如:
    let p = new Promise((resolve, reject) => 
    console.log('first');
    resolve();
    );
    p.then(() => console.log('second'))
    .then(() => console.log('third'))
    .then(() => console.log('fourth'));
    // first
    // second
    // third
    // fourth

第125篇: 期约Promise基本特性

好家伙,本篇为《JS高级程序设计》第十章“期约与异步函数”学习笔记

 

1.非重入期约

1.1.可重入代码(百度百科)

先来了解一个概念

可重入代码(Reentry code)也叫纯代码(Pure code)是一种允许多个进程同时访问的代码。

为了使各进程所执行的代码完全相同,故不允许任何进程对其进行修改。

程序在运行过程中可以被打断,并由开始处再次执行,并且在合理的范围内(多次重入,而不造成堆栈溢出等其他问题),

程序可以在被打断处继续执行,且执行结果不受影响。

                                            ----来自百度百科

 

1.2.非重入期约

非重入期约方法 期约进入 落定(解决/拒绝)状态时,与该状态相关的处理程序不会立即执行 ,

处理程序后的 同步代码 会在其之前 先执行 ,该特性称为非重入

当期约进入落定状态时,与该状态相关的处理程序仅仅会被排期,而非立即执行。

跟在添加这个处 理程序的代码之后的同步代码一定会在处理程序之前先执行。

即使期约一开始就是与附加处理程序关联 的状态,执行顺序也是这样的。

这个特性由 JavaScript 运行时保证,被称为“非重入”(non-reentrancy) 特性。

 

let promise = new Promise(() => 
    console.log("5")
)

// 创建一个期约并将解决函数保存在一个局部变量中
let p = new Promise((resolve) => 
    synchronousResolve = function () 
        console.log(\'1\');
        resolve();
        console.log(\'2\');
    ;
    synchronousResolve();
);
p.then(() => console.log(\'4\'));
console.log(\'3\');

 

看到了吗,这里4依旧被放在了最后输出

这说明在这个例子中,即使期约状态变化发生在添加处理程序之后,处理程序也会等到运行的消息队列让 它出列时才会执行。

 

 

 

2.邻近处理程序的执行顺序

如果给期约添加了多个处理程序,当期约状态变化时,相关处理程序会按照添加它们的顺序依次执行

let p1 = Promise.resolve(); 
p1.then(() => setTimeout(console.log, 0, 1)); 
p1.then(() => setTimeout(console.log, 0, 2)); 
p1.then(() => setTimeout(console.log, 0, 3)); 
p1.then(() => setTimeout(console.log, 0, 4)); 

 

 

 

 

3.期约连锁

把期约逐个地串联起来是一种非常有用的编程模式。

之所以可以这样做,是因为每个期约实例的方 法(then()、catch()和 finally())都会返回一个新的期约对象,

而这个新期约又有自己的实例方,这样连缀方法调用就可以构成所谓的“期约连锁”

你应该能想象到,这就是一长串then(),( then()、catch()和 finally()都行 )

let p = new Promise((resolve, reject) => 
    resolve();
);
p.then(() => console.log(\'1\'))
    .then(() => console.log(\'2\'))
    .then(() => console.log(\'3\'))
    .then(() => console.log(\'4\'));

 

 

 

 

4.期约合成

这肯定是个将多个期约合成为一个的某个特性(废话)

Promise 类提供两个将多个期约实例组合成一个期约的静态方法:Promise.all()和 Promise.race()。

而合成后期约的行为取决于内部期约的行为。


4.1.Promise.all()

特性一:合成的期约只会在每个包含的期约都解决之后才解决

特性二:如果至少有一个包含的期约待定,则合成的期约也会待定。

如果有一个包含的期约拒绝,则合成的 期约也会拒绝

let p1 = Promise.all([
    Promise.resolve(),
    new Promise((resolve, reject) => 
        resolve();
    )
]);
console.log("I\'m p1");
console.log(p1);

// 一次拒绝会导致最终期约拒绝
let p2 = Promise.all([
    Promise.resolve(),
    Promise.reject(),
    Promise.resolve()
]);
console.log("I\'m p2");
console.log(p2);

let p3 = Promise.all([
    Promise.resolve(),
    new Promise(() => ),
    Promise.resolve()
]);
console.log("I\'m p3");
console.log(p3);

 

 

 

 

4.2.Promise.race()

Promise.race()不会对解决或拒绝的期约区别对待。

无论是解决还是拒绝,只要是第一个落定的 期约,Promise.race()就会包装其解决值或拒绝理由并返回新期约

let p1 = Promise.race([
    Promise.resolve(),
    new Promise((resolve, reject) => 
        resolve();
    )
]);
console.log("I\'m p1");
console.log(p1);

// 一次拒绝会导致最终期约拒绝
let p2 = Promise.race([
    Promise.resolve(),
    Promise.reject(),
    Promise.resolve()
]);
console.log("I\'m p2");
console.log(p2);

let p3 = Promise.race([
    Promise.resolve(),
    new Promise(() => ),
    Promise.resolve()
]);
console.log("I\'m p3");
console.log(p3);

 

 

 

 

5.课后练习

 

1.什么是promise期约连锁?

  答:Promise期约连锁调用是使用Promise的一种技术,它可以让你把多个Promise以链式的方式组合起来,每个Promise的结果作为下一个Promise的输入。

这样可以把复杂的操作分解成一系列简单的操作,以便更容易理解和管理。

 

2.如何解释Promise的的非重入特性?

  答:非重入期约方法 期约进入 落定(解决/拒绝)状态时,与该状态相关的处理程序不会立即执行 ,

 

处理程序后的 同步代码 会在其之前 先执行 ,该特性称为非重入

 

以上是关于期约与异步函数的主要内容,如果未能解决你的问题,请参考以下文章

第126篇: 异步函数(async和await)

Promise期约

Java8中Stream的归约与收集

stream — 归约与收集

前端异步流程工具

vue前端异步与页面渲染问题:怎样让子异步函数完成后页面再渲染