3.pomise

pomise

  • promise 的介绍以及基本使用
  • 使用 promise 来请求内容
  • 宏任务与微任务
  • promise 相关 api

promise 的介绍以及基本使用

在昨天,我们学习了使用回调函数来处理异步,但是回调函数有一个很致命的缺点,那就是回调地狱。

promise 最早就是由社区提出来的一种异步编程解决方案,主要用来解决回调地狱。

自从ES6之后,promise被纳入官方标准。

下面我们来看一下 promise简单的示例:

let pm = new Promise(function(resolve,reject){
    // resovle 是在异步操作成功的时候调用
    // reject 是在异步操作失败的时候调用
    // 接下来,我们就可以把我们的异步操作放在 promise 里面
    console.log('Hello');
    resolve(); // resolve 代表异步结束后的下一步操作
    console.log('World');
});

pm.then(function(){
    // 相当于之前回调函数里面嵌套的下一步操作
    console.log('异步结束之后要进行的下一步操作');
})

在执行 promise 的时候,异步的执行结果可以分为两种情况,一种是成功,一种是失败。成功的话会调用 resolve,失败的话会调用reject。

如果想要捕获失败的情况,可以在 then 方法里面传入第二个参数:

let pm = new Promise(function(resolve,reject){
    // resovle 是在异步操作成功的时候调用
    // reject 是在异步操作失败的时候调用
    // 接下来,我们就可以把我们的异步操作放在 promise 里面
    console.log('Hello');
    reject(); // resolve 代表异步结束后的下一步操作
    console.log('World');
});

pm.then(function(){
    // resolve 会进入到第一个 function
    console.log('成功');
},function(){
    // reject 会进入到第二个 function
    console.log('失败');
})

还有一种写法是通过 catch 来捕获失败的情况(推荐)

代码如下:

let pm = new Promise(function(resolve,reject){
    // resovle 是在异步操作成功的时候调用
    // reject 是在异步操作失败的时候调用
    // 接下来,我们就可以把我们的异步操作放在 promise 里面
    console.log('Hello');
    reject(); // resolve 代表异步结束后的下一步操作
    console.log('World');
});

pm.then(function(){
    // resolve 会进入到第一个 function
    console.log('成功');
}).catch(function(){
    // reject 进入到 catch 方法里面
    console.log('失败');
});

无论是异步操作成功还是失败,我们都可以给下一步操作传递数据,代码示例如下:

let pm = new Promise(function(resolve,reject){
    // resovle 是在异步操作成功的时候调用
    // reject 是在异步操作失败的时候调用
    // 接下来,我们就可以把我们的异步操作放在 promise 里面
    console.log('Hello');
    reject("this is a test"); // resolve 代表异步结束后的下一步操作
    console.log('World');
});

pm.then(function(data){
    // resolve 会进入到第一个 function
    // data 来自入上面 resolve 传递过来的参数
    console.log(data);
}).catch(function(data){
    // reject 进入到 catch 方法里面
    console.log(data,'asdsada');
});

一个 promise 对象调用 then 方法之后会返回一个 promise 对象,如下:

let pm = new Promise(function(resolve,reject){
    resolve("this is a test"); // resolve 代表异步结束后的下一步操作
});

pm.then(function(data){
    console.log(data);
}).then(function(){
    console.log('haha');
})

但是上面的代码,有一个缺点,就是后面的 then 无法通过 resolve 传递数据,所以我们可以采取手动返回一个 promise,然后就可以继续往后面传递参数了。

let pm = new Promise(function(resolve,reject){
    resolve("this is a test"); // resolve 代表异步结束后的下一步操作
});

pm.then(function(data){
    console.log(data);
    return new Promise(function(resolve,reject){
        resolve('haha')
    })
}).then(function(data){
    console.log(data);
})

学习完 promise 的基本语法之后,接下来我们就要使用 promise 来解决回调地狱问题:

使用回调的代码如下:

setTimeout(function(){
    console.log(1);
    setTimeout(function(){
        console.log(2);
        setTimeout(function(){
            console.log(3);
        },2000);
    },2000);
},2000);

使用 promise来改写的代码如下:

new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log(1);
        resolve(2);
    }, 2000)
}).then(function (data) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(data);
            resolve(3);
        },2000)
    })
}).then(function(data){
    setTimeout(function () {
        console.log(data);
    },2000);
})

读取文件的例子:

回调的形式,读取文件的代码如下:

const fs = require('fs'); // 引入 fs 模块
fs.readFile('./file1.txt','utf8',function(err,data){
    if(err) throw err;
    console.log(data);
    fs.readFile('./file2.txt','utf8',function(err,data){
        if(err) throw err;
        console.log(data);
        fs.readFile('./file3.txt','utf8',function(err,data){
            if(err) throw err;
            console.log(data);
        });
    });
});

使用 promise 读取文件内容如下:

const fs = require('fs');
const arr = [];

new Promise((resolve, reject) => {
    fs.readFile('./file.txt', 'utf8', (err, data) => {
        if (err) throw err;
        console.log(data);
        arr.push(data);
        resolve();
    })
}).then(() => {
    return new Promise((resolve, reject) => {
        fs.readFile('./file2.txt', 'utf8', (err, data) => {
            if (err) throw err;
            console.log(data);
            arr.push(data);
            resolve();
        })
    })
}).then(() => {
    fs.readFile('./file3.txt', 'utf8', (err, data) => {
        if (err) throw err;
        console.log(data);
        arr.push(data);
        console.log(arr);
    })
})

使用 promise 来请求内容

// 需要以服务器的形式来查看,可以使用 live server

        new Promise((resolve, reject) => {
            $.ajax({
                url: './stu.json',
                success: (data) => {
                    // 找出韩梅梅所在的班级id
                    let classId = null;
                    for (let i of data.student) {
                        if (i.name === '韩梅梅') {
                            classId = i.classId;
                        }
                    }
                    resolve(classId);
                }
            })
        }).then((classId) => {
            return new Promise((resolve, reject) => {
                $.ajax({
                    url: './classes.json',
                    success: (data) => {
                        let teacherId = null;
                        for (let i of data.classes) {
                            if (i.id === classId) {
                                teacherId = i.teacherId;
                            }
                        }
                        resolve(teacherId);
                    }
                });
            });
        }).then((teacherId) => {
            $.ajax({
                url: './teacher.json',
                success: (data) => {
                    for (let i of data.teachers) {
                        if (i.id === teacherId) {
                            console.log(`韩梅梅的班主任为${i.name}`);
                        }
                    }
                }
            });
        })

宏任务与微任务

到目前为止,我们已经知道了promise的一个基本使用方法,我们也知道了整个同步和异步任务代码执行的流程。

但是异步任务里面又要分类,分成宏任务和微任务。

常见的宏任务:

I/O 操作,setTimeout,setInterval,setImmediate,requestAnimationFrame

常见的微任务:

process.nextTick,Promise.then catch finally

执行流程:js 代码的执行顺序为,先执行主线程里面的代码,当遇到一个宏任务或者微任务就会扔给异步处理模块。

主线程的同步任务执行完成后,首先将所有的微任务全部执行一遍,接下来取出一个宏任务的执行结果来执行,再接下来又执行所有的微任务。

实战演练:

console.log(1);
setTimeout(function(){
    console.log(2);
    process.nextTick(function(){
        console.log(3)
    })
    new Promise(function(resolve){
        console.log(4);
        resolve();
    }).then(function(){
        console.log(5);
    })
})
process.nextTick(function(){
    console.log(6);
})
new Promise(function(resolve){
    console.log(7);
    resolve()
}).then(function(){
    console.log(8);
})

// 解析:
// 首先肯定是先输出 1,因为 console.log 是同步任务
// 接下来 setTimeout 是宏任务,会被扔到异步处理模块
// 接下来 process.nextTick 也是一个异步任务,而是是微任务
// 接下来输出 7,因为在 promise 构造里面其实是同步代码,then 后面的任务就会被放到任务队列里面(微任务)
// 整理:目前输出 1,7  宏任务队列 setTimeout  微任务队列: process.nextTick  、 then
// 接下来先执行所有的微任务:打印输出 6、8
// 在接下来,拿出一个宏任务来执行,首先打印输出 2,后面的 process.nextTick 会被放入到微任务队列,接下来的 promise,打印输出 4
// 整理:目前输出 1,7,6,8,2,4
// 最后清空微任务队列,打印输出 3,5
// 整个结果的顺序为 1,7,6,8,2,4,3,5

课堂练习:

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(function () {
            console.log('xixi');
        }, 0);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0);
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg, 'bb');
    });
    setTimeout(() => {
        console.log('haha');
    }, 0);
}));
first().then((arg) => {
    console.log(arg, 'aa');
    setTimeout(() => {
        console.log('heihei');
    }, 0);
});
setTimeout(() => {
    console.log('yoyo');
}, 0);
console.log(4);

// 解析:
// 首先打印输出 3,接下来是 promise,所以打印输出 7,两个 setTime放入到宏任务队列
// resolve(1) 所对应的 then 方法放入到微任务队列,resolve(2) 放入到微任务队列,最后一个 setTimeout 放入到宏任务队列
// 整理:输出 3,7
// 宏任务:setTimeout(xixi) setTimeout(5) setTimeout(haha)
// 微任务:resolve(1) resolve(2)
// setTimeout(yoyo) 会被放入到宏任务,执行最后一句同步代码,打印输出 4
// 整理:输出 3,7,4
// 宏任务:setTimeout(xixi) setTimeout(5) setTimeout(haha) setTimeout(yoyo)
// 微任务:resolve(1) resolve(2)
// 接下来先执行 resolve(1),这里要注意要找到对应的 then 方法,打印输出 1 bb
// 接下来执行 resolve(2),打印输出 2 aa,setTimeout(heihei) 被放入到宏任务
// 整理:输出 3,7,4,1 bb,2 aa
// 宏任务 setTimeout(xixi) setTimeout(5) setTimeout(haha) setTimeout(yoyo) setTimeout(heihei)
// 微任务已经完全清空,接下来就只需要挨着执行宏任务
// 打印输出 xixi,5,haha,yoyo,heihei
// 完整的顺序:3,7,4,1 bb,2 aa,xixi,5,haha,yoyo,heihei

promise 相关 api

Promise.all

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

经常用于处理多个并行的异步操作

接收一个数组,数组里面是多个 promise

示例:

let p1 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(1);
    },1000)
})
let p2 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(2);
    },5000)
})
let p3 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(3);
    },200)
})
Promise.all([p1,p2,p3]).then(function(data){
    console.log(data);
})

在上面的代码中,我们创建了 3 个promise,然后通过 Promise.all 来包装成一个新的 promise对象,

可以看到,3个 promise异步花费的时间是不一样的,但是 then 方法打印结果是按照数组的顺序来打印

课堂练习:使用 Promise.all 书写读取文件的示例

const fs = require('fs');
const p1 = new Promise(resolve=>{
    fs.readFile('./file1.txt','utf8',(err,data)=>{
        if(err) throw err;
        resolve(data);
    })
})
const p2 = new Promise(resolve=>{
    fs.readFile('./file2.txt','utf8',(err,data)=>{
        if(err) throw err;
        resolve(data);
    })
})
Promise.all([p1,p2]).then(function(data){
    console.log(data);
})

需要注意,在多个并行的 promise 中,如果有一个 promise 的状态为 reject,那么最终的状态就为 reject

Promise.race

和上面的 all 很相似,同样是将多个 promise 实例,包装成一个 promise 实例。

但是区别在于 race 方法返回的是各个异步操作中最先结束的异步

实例如下:

let p1 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(1);
    },1000)
})
let p2 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(2);
    },5000)
})
let p3 = new Promise(function(resolve,reject){
    setTimeout(()=>{
        resolve(3);
    },200)
})
Promise.race([p1,p2,p3]).then(function(data){
    console.log(data);
})

Promise.resolve

可以将现有的对象包装成 promise 对象

Promise.resolve('Hello')

等价于

new Promise(resolve=>resolve('Hello'))

示例如下:

const p = Promise.resolve('Hello');

// 等价于
// new Promise(resolve=>resolve("Hello"))

p.then(data=>{
    console.log(data); // Hello
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值