前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Javascript[0x07] -- Promise初探

Javascript[0x07] -- Promise初探

作者头像
江涛学编程
发布2020-06-19 16:41:07
5260
发布2020-06-19 16:41:07
举报
文章被收录于专栏:江涛的博客

三种状态

  • pending
  • resolved(Fulfilled)
  • rejected

注意: 状态一旦转换将不可逆, 返回的是一个promise对象,并不是所有都支持promise

一些方法

  • Promise.then([onFulfilled], [onRejected])
  • Promise.catch(onRejected)
  • Promise.race(iterable)
  • Promise.all(iterable)
  • Promise.reslove(obj)返回一个Promise对象,[[PromiseStatus]]为resolved,[[PromiseValue]]为obj,没有就是undefined。
  • Promise.reject(err)返回一个Promise对象,[[PromiseStatus]]为rejected,[[PromiseValue]]为err,没有就是undefined。
  • Promise.finally()无论resolve还是reject都会触发

一些实现了promises/A+规范的库

  • bluebird
  • q
  • rsvp
  • vow
  • when

don't say, show me the code!!!

code 1

promise中then()会放到异步执行事件循环中,所以先往后执行,然后再执行事件循环队列中的。

代码语言:javascript
复制
let promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
});

promise.then(() => {
    console.log("3");
});

console.log(4);

// 1 2 4 3

这里创建了一个Promise对象,然后先打出1,把resolve放入微队列,打出3,then()后放到微队列,然后打出4,再打出2。

代码语言:javascript
复制
let a = new Promise((resolve, reject) => {
    console.log(1);
    resolve(2);
    console.log(3);
})

a.then((res) => {
    console.log(res);
})
console.log(4);
//1 3 4 2
code 2

promise一旦状态转换是不可逆的,比如说从pending到resolved或者到rejected,而且只能resolve或者reject一次,后面的会被忽略。

代码语言:javascript
复制
let promise = new Promise((resolve, reject) => {
    resolve("a");
    reject("b");
    resolve("c");
});

promise.then((res) => {
    console.log("then: ", res);
}).catch((err) => {
    console.log("catch: ", err);
})

// then: a
code 3

Promise.resolve满足条件接收参数是一个原始值或者不具有then方法的对象,则返回一个新的Promise对象,状态时resolved。而then方法接收的参数是一个函数,所以会被解释成then(null)

代码语言:javascript
复制
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log);
//1
code 4

亮灯问题:红灯3秒亮一次,绿灯2秒亮一次,黄灯1秒亮一次,根据楼下已有的代码,实现下!

代码语言:javascript
复制
//红灯3秒亮一次,绿灯2秒亮一次,黄灯1秒亮一次,请开始你的表演!

function red() {
    console.log("红灯亮!");
}

function green() {
    console.log("绿灯亮!");
}

function yellow() {
    console.log("黄灯亮!");
}

第一次看没啥思路,看到秒脑子里反射出定时器,然后应该是递归的因为它没说什么时候停,那第一步实现一个函数传入两个参数一个时间,一个函数,然后多少秒执行这个函数;第二步把具体的秒数对于具体的函数。

代码语言:javascript
复制
//红灯3秒亮一次,绿灯2秒亮一次,黄灯1秒亮一次,请开始你的表演!

function red() {
    console.log("红灯亮!");
}

function green() {
    console.log("绿灯亮!");
}

function yellow() {
    console.log("黄灯亮!");
}

let light = (timer, cb) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            cb();
            resolve();
        }, timer);
    })
};


let gao_light = () => {
    Promise.resolve().then(() => {
        return light(3000, red);
    }).then(() => {
        return light(2000, green);
    }).then(() => {
        return light(1000, yellow);
    }).then(() => {
        gao_light();
    });
};

gao_light();
code 5

promise怎么进行流程控制: 实现一个函数,输出如楼下

代码语言:javascript
复制
const timeout = ms => new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, ms);
});

const ajax1 = () => timeout(2000).then(() => {
    console.log('1');
    return 1;
});

const ajax2 = () => timeout(1000).then(() => {
    console.log('2');
    return 2;
});

const ajax3 = () =>  timeout(2000).then(() => {
    console.log('3');
    return 3;
});

const mergePromise = ajaxArray => {
    // please complete coding!

}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
    console.log('done');
    console.log(data); // [1, 2, 3]
})

如果说按正常执行那么结果肯定不符合期望,一定是要链式的,这样才符合顺序,所以,我们要做的是怎样把它放到一条链上,这里用了等号赋值,其实相当于延长了Promise链式调用。

代码语言:javascript
复制
const timeout = ms => new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, ms);
});

const ajax1 = () => timeout(2000).then(() => {
    console.log('1');
    return 1;
});

const ajax2 = () => timeout(1000).then(() => {
    console.log('2');
    return 2;
});

const ajax3 = () =>  timeout(2000).then(() => {
    console.log('3');
    return 3;
});

const mergePromise = ajaxArray => {
    // please complete coding!
    let data = [];
    let pr = Promise.resolve();
    ajaxArray.forEach(item => {
        pr = pr.then(item).then(res => {
            data.push(res);
            return data;
        })
    });
    return pr;
}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
    console.log('done');
    console.log('data: ', data); // [1, 2, 3]
})
code 6

从宏任务和微任务的角度分析楼下代码的输出:

代码语言:javascript
复制
const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

假设程序开始跑了,从最上面我们创建了一个Promise对象,所以先打出7这个没问题,往下走又创建了一个Promise对象,打出7也没问题,发现setTimeout宏任务添加到事件队列,resolve(1)也添加到微任务事件队列,往下走resolve(2)添加到微任务事件队列,然后p.then()和first().then()放入微队列,打出4,然后回去打出1,再打出2,第一轮循环事件结束;然后执行setTimeout打出5,由于Promise状态已经改变,6不打出。

code 7

Promise对象先resolve(1),然后接着执行then()方法,在这里我们可以知道会打出1,然后其返回了2,由于不是reject所以catch不到,往下走那么也就是打出2。

代码语言:javascript
复制
Promise.resolve(1).then((res) => {
    console.log(res);
    return 2;
}).catch((err) => {
    console.log(err);
}).then((res) => {
    console.log(res);
})  // 1 2
code 8

首先创建了一个Promise对象,里面是一个宏任务,第一点我们明确的是打出once再现,然后执行异步任务,注意两次输出的res的值,promise状态只能改变一次所以都是success,但是打出的时间戳差值每台电脑可能不一样的,能肯定的是在1秒后也就是1000+。

代码语言:javascript
复制
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('once');
        resolve('success: ');
    }, 1000);
})

const start = Date.now();
promise.then((res) => {
    console.log(res, Date.now() - start);
})

promise.then((res) => {
    console.log(res, Date.now() - start);
})
code 9

这题非常好,非常有意思。就是说我们先创建了一个Promise对象p1,里面有个宏任务setTimeout,然后创建了一个基于p1的Promise对象p2。我们先看这个时候p1的状态是pending,那么p2自然也是,所以先打出的是两个pending状态的promise对象,之后两秒后各状态就位,一个resolved,一个rejected!

代码语言:javascript
复制
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success');
    }, 1000);
});

const p2 = p1.then(() => {
    throw new Error('Error!');
});

console.log('p1: ', p1);
console.log('p2: ', p2);

setTimeout(() => {
    console.log('p1: ', p1);
    console.log('p2: ', p2);
}, 2000);
code 10

这题也很有意思,就是说promise的.then()和.catch()不能返回promise本身,因为会造成死循环。

代码语言:javascript
复制
const promise = Promise.resolve().then(() => {
    return promise;
});

promise.catch((err) => {
    console.log(err);
});
code 11

这题依旧很有意思,process.nextTick()和Promise.then()都属于微任务,而setImmediate属于宏任务,所以这里先打出end,然后执行微任务队列nextTick和then,最后打出setImmediate

代码语言:javascript
复制
process.nextTick(() => {
    console.log('nextTick');
});

Promise.resolve().then(() => {
    console.log('then');
})

setImmediate(() => {
    console.log('setImmediate');
});

console.log('end');
code 12

有意思的,我要是catch了多次会是什么样的一个情况,从中可以看出其实它的工作方式像是一个监听器

代码语言:javascript
复制
const promise = new Promise((resolve, reject) => {
    reject(Error('我就是试试!'));
});

promise.catch(err => console.error(err));
promise.catch(err => console.error(err));
code 13

和楼上做个对比,我要是没有resolve,也没有reject,而是返回了一个新的Promise那么又是如何呢?会打出两次 UnhandledPromiseRejectionWarnin。

代码语言:javascript
复制
const promise = new Promise((resolve, reject) => {
    return new Promise((resolve, reject) => {
        reject(Error('我就是试试!'));
    })
});

promise.catch(err => console.error(err.message));
promise.catch(err => console.error(err.message));
code 14

和code12做个对比,这里它只打出一次。可以看到一个Promise对象可以有一个catch,但如果说是一个Promise实例,那它可以进行多次catch。

代码语言:javascript
复制
const promise = new Promise((resolve, reject) => {
        reject(Error('我就是试试!'));
}).catch(err => console.error(err.message))
  .catch(err => console.error(err.message));

一些缺点

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

相关应用

  • 需求一:从一个存放链接的数组中去下载图片,要求任意时刻下载数不超过三个,下吧,越快也好!(Promise.race()返回最快那个,Promise.all(),他们的灵活运用)

链接:https://segmentfault.com/a/1190000016848192 的题目七

也可以看下这个:https://www.cnblogs.com/xuning/p/8045946.html

  • 需求二:网页中预加载20张图片资源,分步加载,一次加载10张,两次完成,怎么控制图片请求的并发,怎样感知当前异步请求是否已完成?

参考:https://zhuanlan.zhihu.com/p/29792886

相关网址

Javascript Promise迷你书:http://liubin.org/promises-book/

《Node.js设计模式》基于ES2015+的回调控制流: https://juejin.im/post/5a17b1cbf265da4324802133

Promises/A+: https://promisesaplus.com/

关于ES6中promise的面试题:https://segmentfault.com/a/1190000016848192

ES6Promise面试题:http://www.bslxx.com/m/view.php?aid=1467

十道ES6的Promise面试题:http://www.bslxx.com/m/view.php?aid=1505

MDN-Promise: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

Promise 异步流程控制:https://zhuanlan.zhihu.com/p/29792886

MDN-Promise.all() : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

JavaScript Promises: 9 Questions:https://danlevy.net/javascript-promises-quiz/

阮一峰-Promise对象:http://es6.ruanyifeng.com/#docs/promise

选自《Javascript筑基》系列文章

原文地址:https://github.com/ataola/JavaScript-Tsukuki/blob/master/note/promise-01.md

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江涛学编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 三种状态
  • 一些方法
  • 一些实现了promises/A+规范的库
  • don't say, show me the code!!!
    • code 1
      • code 2
        • code 3
          • code 4
            • code 5
              • code 6
                • code 7
                  • code 8
                    • code 9
                      • code 10
                        • code 11
                          • code 12
                            • code 13
                              • code 14
                              • 一些缺点
                              • 相关应用
                              • 相关网址
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档