ES6 学习笔记(十三)promise的简单使用

发布于 2021-06-19  330 次阅读


1、什么是promise

在JavaScript中,我们经常会用到回调函数,而回调函数的使用让我们没法使用return,throw等关键字。JS引用promise正好解决了这个问题。
promise单词意思是承诺,代表未来的某个事情或者是行为。promise是一个容器,包含了异步操作。因此我们认为promise是异步操作的解决方案。所有异步操作都可以通过promise来解决

2、了解promise

上面说到promise是一个容器,那我们先在控制台打印一下。
在这里插入图片描述
可以看到,Promise是一个构造函数,自身就有all、reject(代表失败的回调函数, 失败的操作)、resolve(代表成功的回调函数, 成功的操作)这几个于我们而言比较眼熟的方法,原型上有then、catch等同样很眼熟的方法。这么说用Promise new出来的对象肯定就有then、catch方法。

2.1、promise对象的特点

  • Promise对象的状态不受外界影响。包含pending(进行中)、fulfilled(已成功)和rejected(已失败)三种状态。只有异步操作结果才可决定是哪种状态,其他任何操作都无法改变这种状态。
  • 一旦状态改变,就不会再变。Promise对象的状态改变,只有两种情况:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就不会再变了,这时就称为 resolved(已定型)

举个例子:

let action = new Promise(function (resolve, reject) {
    // 包含一个异步操作
    setTimeout(function () {
        let data = Math.floor(Math.random() * 100);
        console.log(data);
        if (data > 50) {
            resolve({
                msg: '成功',
                data,
            });
        } else {
            reject({
                msg: '失败',
                data,
            });
        }
    }, 30)
});
console.log(action); // pending -- 进行中

// action -- 执行完的事件
action.then(function (d) {
    // 参数1 - 成功回调
    console.log(action); // resolved -- 已定型(fulfilled --- 已成功)
    console.log(d);
}, function (d) {
    // 参数2 - 失败回调(可选)
    console.log(action); // rejected -- 已失败
    console.log(d);
});

输出结果:
Promise { }
52
Promise { { msg: '成功', data: 52 } }
{ msg: '成功', data: 52 }

Promise也有一些不足的地方。其一,一旦新建Promise就会立即执行,无法取消。如果不设置回调函数,Promise内部抛出的错误不会反应到外部。其二,当处于pending状态时,无法得知目前是哪个阶段。

2.2、了解宏任务和微任务

先看个例子:

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

总结:
宏任务指的是click,setTimeOut等DOM事件 微任务指的是promise的异步操作,数据查询
宏任务要比微任务先进栈,但是后出栈(微任务比宏任务先执行)。因此上述代码中setTimeout函数最后执行。

了解更多有关宏任务和微任务的内容

3、使用promise

3.1 异步加载图片

function loadImageAsync(url){
    return new Promise(function(resolve,reject){
        var image = new Image();
        image.onload = function(){
            resolve(this)
        };
        image.onerror = function(){
            reject(new Error('Counld not load image at'+url))
        }
        image.src = url
        document.body.appendChild(image)
    })
}
let urlimg = 'https://static.likepoems.com/cdn/sakurairo/logo.jpg'
loadImageAsync(urlimg).then(function(image){
    console.log(`Image loaded:${image.src}`);
},function(error){
    console.log(error);
})

3.2 使用Promise封装ajax

function getData(url) {
    let promise = new Promise(function (resolve, reject) {
        // 异步操作 - 做数据请求
        let ajax = new XMLHttpRequest();
        ajax.open('get', url);
        ajax.send();
        ajax.onreadystatechange = function () {
            if (ajax.readyState === 4) {
                if (ajax.status === 200) {
                    // 成功
                    resolve(ajax.responseText);
                } else {
                    // 失败
                    reject(ajax);
                }
            }
        }
    });
    return promise;
}

// 使用
getData('./1.txt').then(function (d) {
    console.log(d);
    console.log('成功');
}, function (err) {
    console.log(err);
    console.log('失败');
});

运行结果:
在这里插入图片描述

3.3 then方法与链式操作

举个例子:

2.txt

{
    "name":"李明",
    "sex":"男",
    "url":"./2.txt"
}
getData('./2.txt').then(function (d) {
    console.log('步骤1', JSON.parse(d).url);
    return getData(JSON.parse(d).url);
}).then(function (d) {
    console.log('步骤2', JSON.parse(d).url);
    return getData(JSON.parse(d).url);
}).then(function (d) {
    console.log('步骤3', JSON.parse(d).url);
    return getData(JSON.parse(d).url);
})

输出结果:
步骤1 ./2.txt
步骤2 ./2.txt
步骤3 ./2.txt

Promise实例的then( )可以一直调用下去。

3.4 出现错误的处理

getData('./2.txt').then(function (d) {
    console.log('步骤1', JSON.parse(d).url);
}).then(function (d) {
    console.log('步骤2', JSON.parse(d).url);
    resolve();
}).then(function (d) {
    throw new Error("Error");
    console.log("then");
}).catch(function (err) {
    console.log("catch error:" + err.message);
});

输出结果:
步骤1 ./2.txt
catch error:Unexpected token u in JSON at position 0

总结:
若遇到异常抛出,会顺着promise链寻找下一个onRejected失败回调函数或者由catch指定的回调函数。我们一般使用catch来终止promise链,避免链条中的rejection抛出错误到全局

3.5 Promise.all()使用方法

举个例子:

let p1 = new Promise((res, rej) => {
    setTimeout(() => {
        res("p1")
    }, 1000)
})
let p2 = new Promise((res, rej) => {
    setTimeout(() => {
        res("p2")
    }, 2000)
})
let p3 = new Promise((res, rej) => {
    setTimeout(() => {
        res("p3")
    }, 3000)
})
// 接收多个promise对象并返回一个promise对象
Promise.all([p1, p2, p3]).then((r) => {
    console.log(r);
}).catch((err) => {
    console.log(err.message);
})

输出结果:
[ 'p1', 'p2', 'p3' ]

总结: Promise.all( )实参是所有Promise实例的字面量组成的数组,执行完毕的结果是所有输出结果的所组成的数组。

4、拓展:async/await

4.1 async的使用

async函数使得异步操作变得更加方便

// async函数会返回一个Promise对象
async function hello() {
    return "helloworld"
}
console.log(hello());
hello().then((r)=>{
    console.log(r);
})

输出结果:
Promise { 'helloworld' }
helloworld

4.2 await的使用

await可以让代码暂停下来等待异步任务完成,await关键字只能放在async标识的异步函数里面才能够生效。
举个例子:

function getNumber() {
    return new Promise((res, rej) => {
        setTimeout(() => {
            res(12)
        }, 2000)
    })
}
async function test() {
    let r = await getNumber().then(v => v)
    r++
    return r
}
test().then(v => console.log(v))

输出结果:
13

function getNumber() {
    return new Promise((res, rej) => {
        setTimeout(() => {
            res(12)
        }, 2000)
    })
}
let r = await getNumber().then(v => v)
console.log(r);

输出结果:
SyntaxError: await is only valid in async function(语法错误)


活的像诗一样