ES6(Promise)

Promise的概念

Promise对象用于表示一个异步操作的最终状态(完成或失败)以及其返回的值 --- MDN

回调与Promise

模拟场景

实现一个功能:以1秒的间隔依次显示1,2,3,4

传统回调
// 方式1:传统回调方式
function f(cb) {
    setTimeout(function () {
        cb && cb();
    }, 1000);
}

f(function () {
    // 第一层回调中的处理
    console.log(1);

    f(function () {
        // 第二层回调中的处理
        console.log(2);

        f(function () {
            // 第三层回调中的处理
            console.log(3);

            f(function () {
                // 第四层回调中的处理
                console.log(4);
            });
        });
    });
});

// 执行结果:以1秒的间隔依次显示1,2,3,4

这里只是举个例子,所有每一次层的处理都很简单,只是显示一个数字。
在实际业务中,每一次层的处理会很复杂。然后如果有需求想要交换下第二层和第三层的处理,该怎么弄?即使对业务逻辑很清楚,这事情处理起来也简直就是地狱。。甚至如果还有5678层呢?

Promise
// 方式2:Promise方式
function f(cb) {
    // 说明
    // 返回一个Promise实例
    // 参数resolve代表成功时候要做的事情
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, 1000);
    });
}

// 说明
// 由于f返回的是一个Promise实例,所以拥有then方法
// then方法里就是实际要做的事情
f()
    .then(() => {
        console.log(1);
        return f();  // 这里需要返回f(),这样就能继续then下去了
    })
    .then(() => {
        console.log(2);
        return f();  // 这里需要返回f(),这样就能继续then下去了
    })
    .then(() => {
        console.log(3);
        return f();  // 这里需要返回f(),这样就能继续then下去了
    })
    .then(() => {
        console.log(4);
    })
// 执行结果:以1秒的间隔依次显示1,2,3,4
// 这和传统回调方式的结果是一样的

可以看到,以Promise的方式处理这个问题,不是一层嵌一层的样子了,而是一步做完再接着做下一步。是不是比传统的回调方式好理解很多?交换某两个处理的顺序也会很方便。
新事物的出现一定有它的理由和好处,Promise就是为了解决回调地狱而出现的。非常友好,值得我们拥抱它。

错误处理

then(resolve, reject)
// then方法中的第二个回调reject,就是失败时候做的事情
function f(a) {
    return new Promise((resolve, reject) => {
        if (a) {
            resolve();
        } else {
            reject();
        }
    })
}

f(false)
    .then(() => {
        console.log('我是成功时候的处理');
    }, () => {
        console.log('我是失败时候的处理');
    });

// 执行结果:我是失败时候的处理
catch
// 使用实例的catch方法,可以捕获错误
function f(a) {
    return new Promise((resolve, reject) => {
        if (a) {
            resolve();
        } else {
            reject();
        }
    })
}

f(false)
    .then(() => {
        // 这里不会执行到
        console.log('我是成功时候的处理');
        return f(false);
    })
    .catch(()=>{
        console.log('我是失败时候的处理');
    });

// 执行结果:我是失败时候的处理
finally
// 不伦成功还是失败,finally中的内容一定会执行
// 跟最终的处理一起执行
function f(a) {
    return new Promise((resolve, reject) => {
        if (a) {
            resolve();
        } else {
            reject();
        }
    })
}

f(false)
    .then(() => {
        // 这里不会执行到
        console.log('我是成功时候的处理');
        return f(false);
    })
    .catch(() => {
        console.log('我是失败时候的处理');
        return f(true);
    })
    .finally(() => {
        console.log('finally');
    });

// 执行结果:我是失败时候的处理 finally

Promise的三种状态

  • pending(进行中)
  • fulfilled(成功)
  • rejected(失败)

状态的改变不可逆,一旦决议就不能再修改。且只有以下两种改变形式。
pending(进行中) => fulfilled(成功)
pending(进行中) => rejected(失败)

Promise方法

Promise.all()

Promise.all方法可以把多个promise实例,包装成一个新的promise实例。
Promise.all([promise1,promise2,promise3]):Promise

特点1:当promise1,promise2,promise3都成功的时候,Promise.all就决议为成功,且返回由promise1/promise2/promise3的结果组成的数组。

// 模拟需要多个请求的数据,才能进行下一步操作的情况
function getData1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第一条数据加载成功!');
            resolve('data1');
        }, 1000);
    });
}

function getData2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第二条数据加载成功!');
            resolve('data2');
        }, 1000);
    });
}

function getData3() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第三条数据加载成功!');
            resolve('data3');
        }, 2000);
    });
}

let p = Promise.all([getData1(), getData2(), getData3()]);
p.then(arr => {
    console.log(arr);
});

// 执行结果:
// 1s之后显示:"第一条数据加载成功!" 和 "第二条数据加载成功!"
// 2s之后显示:"第三条数据加载成功!"
// p.then中的arr为 ["data1", "data2", "data3"]

特点2:当promise1,promise2,promise3之中有一个失败的时候,Promise.all就决议为失败,且返回失败的信息。

// 模拟需要多个请求的数据,才能进行下一步操作的情况
function getData1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第一条数据加载成功!');
            resolve('data1');
        }, 1000);
    });
}

function getData2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第二条数据加载成功!');
            resolve('data2');
        }, 1000);
    });
}

function getData3() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第三条数据加载失败!');
            reject('err data3');  // !!注意这里调用的是reject,表示失败
        }, 2000);
    });
}

let p = Promise.all([getData1(), getData2(), getData3()]);
p
    .then(arr => {
        // 这里永远不会执行到
        console.log(arr);
    })
    .catch(err => {
        console.log(err);
    });

// 执行结果:
// 1s之后显示:"第一条数据加载成功!" 和 "第二条数据加载成功!"
// 2s之后显示:"第三条数据加载失败!"
// p.catch中的err为 "err data3"

特点3:当Promise.all([])的时候,Promise.all就直接决议为成功。

let p = Promise.all([]);

p
    .then(arr => {
        console.log("成功");
    })
    .catch(err => {
        console.log("失败");
    });

// 执行结果:成功
Promise.race()

顾名思义,race是竞赛的意思。
所以Promise.race方法,是把多个promise实例中,最先成功或失败的结果拿出来。
Promise.race([promise1,promise2,promise3]):Promise

  • 成功的情况
function getData1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第一条数据加载成功!');
            resolve('data1');
        }, 1000);
    });
}

function getData2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第二条数据加载成功!');
            resolve('data2');
        }, 1500);
    });
}

function getData3() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第三条数据加载成功!');
            resolve('data3');
        }, 500);
    });
}

let p = Promise.race([getData1(), getData2(), getData3()]);

p.then(data => {
    console.log(data);  // data3
});
// 执行结果:
// 0.5s之后显示:"第三条数据加载成功!"
// p.then中的data为 "data3"
// 1s之后显示:"第一条数据加载成功!"
// 1.5s之后显示:"第二条数据加载成功!"
  • 失败的情况
function getData1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第一条数据加载成功!');
            resolve('data1');
        }, 1000);
    });
}

function getData2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第二条数据加载成功!');
            resolve('data2');
        }, 1500);
    });
}

function getData3() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('第三条数据加载失败!');
            reject('err data3');
        }, 500);
    });
}

let p = Promise.race([getData1(), getData2(), getData3()]);

p.then(data => {
    // 这里不会被执行到
    console.log(data);
}).catch(data => {
    console.log(data);  // err data3
});
// 执行结果:
// 0.5s之后显示:"第三条数据加载失败!"
// p.then中的data为 "err data3"
// 1s之后显示:"第一条数据加载成功!"
// 1.5s之后显示:"第二条数据加载成功!"
Promise.resolve()

常用来生成已经被决议为成功的Promise实例

function f(a) {
    return new Promise((resolve, reject) => {
        if (a) {
            resolve();
        } else {
            reject();
        }
    })
}

f(true)
    .then(() => {
        console.log('处理1');
        return Promise.resolve();  // 这里就相当于  return f(true);
    })
    .then(() => {
        console.log('处理2');
    })
    .catch(() => {
        console.log('错误');
    });

// 执行结果:处理1 处理2
Promise.reject()

常用来生成已经被决议为失败的Promise实例

function f(a) {
    return new Promise((resolve, reject) => {
        if (a) {
            resolve();
        } else {
            reject();
        }
    })
}

f(true)
    .then(() => {
        console.log('处理1');
        return Promise.reject();  // 这里就相当于 return f(false)
    })
    .then(() => {
        console.log('处理2');
    })
    .catch(() => {
        console.log('错误');
    });

// 执行结果:处理1 错误
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342