ES6(二):Promise

版权声明:本文为博主原创文章,未经博主允许不得转载。

ES6之前解决异步编程只能使用回调函数或事件,ES6中加入了 Promise,使得异步编程更加简洁直观和合理

特点

Promise是一个对象,具有以下两个特点:

  • 对象的状态不受外界影响
  • 状态一旦改变就不会再变

使用方法

基本使用

ES6中规定,Promise对象是一个构造函数,于是我们就需要使用new关键字实例化:

code:

const promise = new Promise((resolve, reject) => {
    if (true) {
        resolve(successRes);
    } else {
        reject(errorText);
    }
});

Promise接受一个函数作为参数,该函数的两个参数分别是:resolvereject。其中:

resolve可以表示异步操作成功时调用 reject则可以表示异步操作失败时调用

then

Promise实例生成之后,可以使用then方法分别指定成功和失败状态的回调函数。例如:

code

let a = 10;
const promise = new Promise((resolve, reject) => {
    if (a === 10) {
        resolve('成功!');
    } else {
        reject('失败!');
    }
});
promise.then((res) => {
    console.log(res); // 成功!
}, (err) => {
    console.log(err);
});
let a = 0;
const promise = new Promise((resolve, reject) => {
    if (a === 10) {
        resolve('成功!');
    } else {
        reject('失败!');
    }
});
promise.then((res) => {
    console.log(res);
}, (err) => {
    console.log(err); // 失败!
});

当然,then的第二个参数并不是必须的,大部分时候我们其实都只需要第一个参数(成功),而失败的回调可以放在catch中去执行。

catch

比如上面返回‘失败’的例子,我们可以使用catch进行改造:

code

let a = 10;
const promise = new Promise((resolve, reject) => {
    if (a === 10) {
        resolve('成功!');
    } else {
        reject('失败!');
    }
});
promise.then((res) => {
    console.log(res);
}).catch((err) => {
    console.log(err); //失败!
});

大部分时候我们都是这样使用的

Ajax实例

我们可以使用Promise对象实现一个ajax实例,这也是Promise用处最广的地方:

code

const myAjax = function(data, url) {
    const promise = new Promise((resolve, reject) => {
        const stateChange = function() {
            if (this.readyState !== 4) {
                return;
            }
            if (this.status === 200) {
                resolve(this.response);
            } else {
                reject(new Error(this.statusText));
            }
        };
        const myHttp = new XMLHttpRequest();
        myHttp.open('GET', url, 'true');
        myHttp.onreadystatechange = stateChange;
        myHttp.responseType = 'json';
        myHttp.setRequestHeader('Accept', 'application/josn');
        myHttp.send(data);
    });
    return promise;
};

myAjax('/uuurl').then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

其他特性

新建就会立即执行

code

const promise = new Promise(function(resolve, reject) {
    console.log('我是resolve之前的打印');
    resolve();
});
  
promise.then(function() {
    console.log('我是成功的回调的打印');
});
  
console.log('我是最外层的打印');

// 结果:
// 我是resolve之前的打印
// 我是最外层的打印
// 我是成功的回调的打印

结果和我们想象的一致,先是打印resolveconsole,因为Promise一建立就会执行,而后进行的是最外层的打印,那是因为then指定的回调函数将在当前脚本所有同步任务执行完之后才会执行。

一个异步操作的结果是返回另一个异步操作

通常情况下,reject函数的参数是Error对象的实例,表示抛出的错误;而resolve函数的参数除了正常的值以外,还可能是另一个Promise实例: code

const pro1 = new Promise((resolve, reject) => {

});
const pro2 = new Promise((resolve, reject) => {
    resolve(pro1);
});

上述代码中,pro1pro2都是Promise实例,但是pro2resolvepro1作为参数,此时pro1的状态就会传递给pro2,也就是说,pro1的状态决定了pro2的状态。 这一点从以下代码可以很直观的看出来: code

const pro1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        return reject(new Error('err'));
    }, 3000);
});
const pro2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        return resolve(pro1);
    }, 1000);
});

pro2.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

在这段代码中,pro1是一个Promise对象,并且在3秒之后返回Error的实例errpro2的状态则是在1秒之后改变。由于pro2返回的是另一个Promisepro1),导致pro2自己的状态无效了,由pro1的状态决定pro2的状态。所以最终输出的结果是:

过了3秒输出Error:err

调用resolvereject并不会终结Promise的参数函数的执行

先来看一段代码: code

const proromise = new Promise((resolve, reject) => {
    resolve('ok');
    console.log('我是resolve后面的代码');
});

proromise.then(res => {
    console.log(res);
});

// 结果:
// 我是resolve后面的代码
// ok

和预想的一样,虽然先调用了resolve('ok');,但是后面的代码还是会执行,并且会先打印出来,这是因为调用resolvereject并不会终结Promise的参数函数的执行,而且then指定的回调函数将在当前脚本所有同步任务执行完之后才会执行。一般调用resolvereject之后Promise的任务就完成了,所以建议在resolvereject之后加上return

参考链接

《ECMAScript 6 入门》——阮一峰 ECMAScript® 2015 Language Specification ECMAScript® 2016 Language Specification ECMAScript® 2019 Language Specification

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jackson0714

【.Net底层剖析】3.用IL来理解属性

3407
来自专栏java一日一条

JVM 进行线程同步背后的原理

所有的 Java 程序都会被翻译为包含字节码的 class 文件,字节码是 JVM 的机器语言。这篇文章将阐述 JVM 是如何处理线程同步以及相关的字节码。

911
来自专栏青玉伏案

窥探Swift编程之错误处理与异常抛出

在Swift 2.0版本中,Swift语言对其错误处理进行了新的设计,当然了,重新设计后的结果使得该错误处理系统用起来更爽。今天博客的主题就是系统的搞一下Swi...

2045
来自专栏Java帮帮-微信公众号-技术文章全总结

json解析-开发必会

json解析 什么是JSON: JSON即JavaScript Object Natation, 它是一种轻量级的数据交换格式, 与XML一样, 是广泛被采用的...

3896
来自专栏Young Dreamer

简易前端模板引擎

模板解析 解决的问题: 将data和js+html片段解析成html片段,完成赋值和js逻辑,如下所示: 输入: var data = {     name: ...

21710
来自专栏C/C++基础

new和delete的使用规范

C++的动态内存管理是通过new和delete两个操作来完成的,即用new来申请空间,用delete来释放空间。在使用new和delete时,注意以下原则。

1222
来自专栏开源优测

移动测试Appium之API手册

移动测试Appium之API手册 前言 本文对Appium Python Client中webdriver.py代码进行分析说明。 笔者使用python3.6版...

4269
来自专栏架构说

声明和定义的区别(深入理解)

问题 声明和定义区别 definition declared 微信排版支持makdown语法不友好 可以查看原文链接 先看一下 例子1 编译有没有问题? cl...

27410
来自专栏我是攻城师

Solr搜索问题笔记(一)

3774
来自专栏WindCoder

PHP内存中的对象和引用简介

我首次起草这篇文章是在备战我的PHP认证时,以便更好地了解PHP如何管理内存中的变量和对象。经过大量研究,我意识到找到我的问题的答案并不容易,所以一旦我完成了,...

1721

扫码关注云+社区

领取腾讯云代金券