理解【ES6】Promise

什么是Promise

Promise是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。

  • 从语法上讲,Promise是一个对象,从它可以获取异步操作的信息。

代码书写比较

首先封装一个支持Promise的ajax方法:

function request(url,data = {}){
    return new Promise((resolve,reject)=>{
        $.ajax({
            url,
            data,
            success:function (data) {
                resolve(data);
            },
            error:function (error) {
                reject(error);
            }
        })
    });
}。

用 request 方法实现多个互相依赖的网络请求

let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
request(url1)
    .then((data)=>{
        console.log(data);
        return request(url2,data)
    })
    .then((data)=>{
        console.log(data);
        return request(url3,data)
    })
    .then((data)=>{
        console.log(data)
    })
    .catch((error)=>{
        console.log(error);
    });

Promise 的特性

Promise 的状态、

  • pending (等待态)
  • fulfilled (完成态)
  • rejected (拒绝态)

终值与拒因

  • 终值: 指的是promise被解决时传递给解决回掉的值
  • 拒因:拒绝原因,指在 promise 被拒绝时传递给异常回调的拒绝原因

状态与状态关系,状态与终值和拒因的关系

  • pending可以迁移至fulfilled或rejected
  • fulfilled 不能迁移至其他状态,必须拥有一个不可变的终值
  • rejected 不能迁移至其他状态,必须拥有一个不可变的据因

Promise 的使用

构造函数

Promise 是一个构造函数,使用new操作符返回一个promise对象

构造函数接收一个 excutor函数作为参数

excutor函数有两个函数类型的参数resolve和reject

let p = new Promise((resolve,reject)=>{
     // 在 excutor 函数中执行异步操作
     // 当异步操作完成后,调用 resolve 函数或 reject 函数
});
  • 构造函数在调用时,excutor函数会作为同步代码立即执行
  • 我们通常在excutor函数中执行我们的异步操作
  • 未调用resolve、reject函数时,promise对象的状态为pending
let p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('p1');
    },1000);
});
// p1 的状态一直为 pending
  • 当调用resolve函数,resolve的参数为非promise对象,非thenable对象
  1. resolve 函数的参数,作为 promise 对象的终值
  2. promise 对象的状态变为 fulfilled
let p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('p2');
        resolve('我是p2的终值')
    },1000);
});
// 代码执行,1000ms内,p2 的状态为 pending
// 代码执行,1000ms后,p2 的状态为 fulfilled
// 代码执行,1000ms后,p2 的终值为 '我是p2的终值'

  • 当调用 resolve 函数, resolve 的参数为 promise 对象
  1. promise 对象的状态、终值、拒因与传入的 promise 对象同步
let p = new Promise((resolve,reject)=>{
    reject('error')
})
let p1 = new Promise((resolve,reject)=>{
    resolve(p)
})
// p1 的状态为 rejected ,拒因为 error
  • 当调用 resolve 函数, resolve 的参数为 thenable 对象
  1. 会对 thenable 对象进行展开操作,promise 对象的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果
let thenable1 = {
    then:function(resolve,reject){
        resolve(1)
    }
}
let thenable2 = {
    then:function(resolve,reject){
        reject(2)
    }
}
let thenable3 = {
    then:function(resolve,reject){
        throw new Error(3)
    }
}
let thenable4 = {
    then:function(fn1,fn2){
        //不调用 fn1 fn2
    }
}
let p1 = new Promise((resolve,reject)=>{
    resolve(thenable1);
})
let p2 = new Promise((resolve,reject)=>{
    resolve(thenable2);
})
let p3 = new Promise((resolve,reject)=>{
    resolve(thenable3);
})
let p4 = new Promise((resolve,reject)=>{
    resolve(thenable4);
})
// p1 的状态为 fulfilled 终值为 1
// p2 的状态为 rejected  终值为 2
// p3 的状态为 rejected  拒因为 Error:3
// p4 的状态为 pending
  • 当调用reject函数,reject函数的参数,作为promise对象的拒因
  • promise对象的状态变为rejected
let p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('p3');
        reject('我是p3的拒因')
    },1000);
});
// 代码执行,1000ms内,p3 的状态为 pending
// 代码执行,1000ms后,p3 的状态为 rejected
// 代码执行,1000ms后,p3 的拒因为 '我是p3的拒因'

Promise对象上的方法

then方法

promise提供一个then方法,用于访问其终值和拒因。
promise的then 方法接受两个参数:

promise.then(onFulfilled, onRejected)
  • onFulfilled函数用于当promise状态变为fulfilled时,接收终值。
  • onRejected函数用于当promise状态变为rejected时,接收拒因
new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('异步任务获取的数据')
    },50)
}).then((data)=>{
    console.log(data)
})
// 异步任务获取的数据

new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject(new Error('异步任务异常'))
    },50)
}).then(null,(error)=>{
    console.log(error)
})
// Error: 异步任务异常
new Promise((resolve,reject)=>{
    throw new Error('抛出一个异常');
}).then(null,(error)=>{
    console.log(error)
})
// Error: 抛出一个异常

onFulfilled 和 onRejected 参数可选

  • 如果 onFulfilled 不是函数,其必须被忽略
  • 如果 onRejected 不是函数,其必须被忽略

onFulfilled 的特性

如果onFulfilled是函数:

  • 当promise执行结束后其必须被调用,其第一个参数为promise的终值
  • 当promise执行结束前其不可被调用
  • 其调用次数不可超过1次

onRejected 的特性

如果onRejected是函数:

  • 当promise执行结束后其必须被调用,其第一个参数为promise的拒因
  • 当promise执行结束前其不可被调用
  • 其调用次数不可超过1次

onFulfilled 和 onRejected 的调用时机

  • 当 promise 对象的状态变为 fulfilled 或 rejected 时调用
  • onFulfilled、onRejected 永远都是异步调用
  • onFulfilled、onRejected 在事件队列中作为微任务来处理
console.log(1);
setTimeout(function(){
    console.log(2)
},0)
new Promise((resolve,reject)=>{
    resolve(3);
}).then((data)=>{
    console.log(data);
})
console.log(4)
// print: 1 4 3 2

onFulfilled 和 onRejected 的调用要求

  • onFulfilled 和 onRejected 必须被作为函数调用
  • 非严格模式下,this 为全局对象
  • 严格模式下,this 为 undefined
function fn1(){
    new Promise((resolve)=>{
        resolve();
    }).then(function(){
        console.log(this)
    })
}
function fn2(){
    "use strict";
    new Promise((resolve)=>{
        resolve();
    }).then(function(){
        console.log(this)
    })
}
fn1(); // print: window
fn2(); // print: undefined

then方法的多次调用

  • then方法可以被同一个promise对象多次调用
  • then方法会返回一个新的promise对象
  • 当promise成功执行时,所有onFulfilled需按照其注册顺序依次回调
  • 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调
let p = new Promise((resolve)=>{
    resolve()
});
let p1 = p.then(()=>{
    console.log('异步执行,第一个onFulfilled');
});
let p2 = p.then(()=>{
    console.log('异步执行,第二个onFulfilled');
});
console.log(p1.constructor === Promise);
console.log(p === p1);
console.log(p === p2);
console.log(p1 === p2);
// print: true
// print: false
// print: false
// print: false
// print: 异步执行,第一个onFulfilled
// print: 异步执行,第二个onFulfilled

then方法的返回值

then方法返回一个promise对象

promise2 = promise1.then(onFulfilled, onRejected);   
  • 若onFulfilled 、onRejected 返回一个非promise
    对象、非thenable对象的值x,则promise2的状态变为fulfilled,终值为x
let p = new Promise((resolve,reject)=>{
    throw new Error();
});
let p1 = p.then(null,(data)=>{
    return '我是p2的终值'
});
p1.then((data)=>{
    console.log(data)
});
// print: 我是p2的终值
  • 若 onFulfilled 、onRejected 返回一个 promise 对象的值 x ,promise2 的状态、终值、拒因与 x 同步
let p1 = new Promise((resolve,reject)=>{
    resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
    reject(2)
})
let p3 = new Promise((resolve)=>{
    resolve()
})
let p4 = p3.then(()=>{
    return p1;
})
let p5 = p3.then(()=>{
    return p2;
})
// p4 的状态为 fulfilled 终值为 1
// p5 的状态为 rejected  拒因为 2

  • 若 onFulfilled 、onRejected 返回一个 thenable 对象 ,会对 thenable 对象进行展开操作,promise2 的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果
let thenable1 = {
    then:function(resolve,reject){
        resolve(1)
    }
}
let thenable2 = {
    then:function(resolve,reject){
        reject(2)
    }
}
let p1 = new Promise((resolve,reject)=>{
    resolve()
})
let p2 = p1.then(()=>{
    return thenable1;
})
let p3 = p1.then(()=>{
    return thenable2;
})
// p2 的状态为 fulfilled 终值为 1
// p3 的状态为 rejected  拒因为 2
  • 若 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 的状态为 rejected,拒因为 e
let p = new Promise((resolve,reject)=>{
    resolve();
});
let p1 = p.then((data)=>{
    throw new Error('error')
});
p1.then(null,(err)=>{
    console.log(err);
});
// print: Error: error
  • 若 onFulfilled 不是函数且 promise1 成功执行, promise2 的状态为 fulfilled 终值为 promise1 的终值
let p = new Promise((resolve,reject)=>{
    resolve('我是p1的终值');
});
let p1 = p.then(null,null);
p1.then((data)=>{
    console.log(data);
});
// print: 我是p1的终值
  • 若 onRejected 不是函数且 promise1 拒绝执行, promise2 的状态为 rejected 拒因为 promise1 的拒因
let p = new Promise((resolve,reject)=>{
    reject('我是p1的拒因');
});
let p1 = p.then(null,null);
p1.then(null,(err)=>{
    console.log(err);
});
// print:我是p1的拒因
  • 若 onFulfilled、onRejected 执行过程中抛出异常,则 promise2 的状态为 rejected 拒因为抛出的异常
let p = new Promise((resolve,reject)=>{
    resolve('我是p的终值');
});
let p1 = p.then((data)=>{
    throw new Error('异常')
});
p1.then(null,(err)=>{
    console.log(err);
});
// print:Error: 异常

终值和拒因的穿透特性

  • 如果 promise 的状态变为 fulfilled,then 方法没有注册 onFulfilled
  1. then 方法返回的 promise 对象的状态变为 fulfilled
  2. then 方法返回的 promise 对象的终值与原 promise 对象的终值相同
  • 如果 promise 的状态变为 rejected,then 方法没有注册 onRejected
  1. then 方法返回的 promise 对象的状态变为 rejected
  2. then 方法返回的 promise 对象的拒因与原 promise 对象的拒因相同
let p1 = new Promise((resolve,reject)=>{
    resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
    reject(2)
})

let p3 = p1.then(null,null);
let p4 = p2.then(null,null);
// p3 的状态是 fulfilled 终值 1
// p4 的状态是 rejected  拒因 2


p5 = p3.then(null,null);
p6 = p4.then(null,null);
// p3 的状态是 fulfilled 终值 1
// p4 的状态是 rejected  拒因 2
  • 穿透特性主要用于异常处理
let fn1 = function(){}
let fn2 = function(){}
let fn3 = function(){}
let fn4 = function(){}
let fn5 = function(){}
let onError = function(){};
new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject()
    })
})
.then(fn1)
.then(fn2)
.then(fn3)
.then(fn4)
.then(fn5)
.then(null,onError)

fn1、fn2、fn3、fn4、fn5 都可能发生错误,通过在最后的then函数注册的 onRejected 函数接收可能发生异常错误

catch方法:

catch(fn) 方法实际上是 then(null,fn) 方法的别名,catch 方法的返回值以及 catch 方法中出现异常的情况与调用 then 方法相同

new Promise((resolve,reject)=>{
    reject()
}).then(null,function(error){

})
// 等同于
new Promise((resolve,reject)=>{
    reject()
}).catch(function(error){

})

Promise 的静态方法

Promise.resolve

Promise.resolve 方法用于将现有数据转换为 promise 对象

  • 若入参为 promise 对象

返回的 promise 对象的状态、终值、拒因与 Promise.resolve 方法的入参同步

  • 若入参为 thenable 对象

会对 thenable 对象进行展开操作,返回的 promise 对象的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果

  • 若入参为非 promise 非 thenable 对象

1.返回的 promise 对象的状态为 fulfilled
2.返回的 promise 对象的终值为 Promise.resolve 方法的入参

let p = Promise.resolve(x)
// 等价于
let p = new Promise((resolve)=>{
    resolve(x)
})

Promise.reject

  • Promise.reject 方法用于返回一个状态为 rejected ,拒因为方法入参的 promise 对象

let p = Promise.reject(x)
// 等价于
let p = new Promise((resolve,reject)=>{
    reject(x)
})

Promise.all

  • Promise.all 方法用于将多个 promise 对象包装成一个新的 promise 对象
const p = Promise.all([p1, p2, p3]);
  • p1、p2、p3 都是 promise 对象,如果不是,调用 Promise.resolve 方法转换为 promise 对象
    p 的状态由 p1、p2、p3 决定

  • 当 p1、p2、p3 的状态都变成 fulfilled

p 的状态为 fulfilled
此时 p1、p2、p3 的终值组成一个数组,这个数组作为 p 的终值

  • 当 p1、p2、p3 的状态有一个变成 rejected

p 的状态变为 rejected
此时第一个状态变为 rejected 的 promise 对象的拒因作为 p 的拒因

let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = 3;

Promise.all([p1,p2,p3]).then((data)=>{
    console.log(data); // print: [1,2,3]
})
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p2 error')
    },1000)
})
let p3 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p3 error')
    },500)
})
Promise.all([p1,p2,p3]).catch((error)=>{
    console.log(error); // print: p3 error
})

Promise.race

  • Promise.race 方法同样用于将多个 promise 对象包装成一个新的 promise 对象
const p = Promise.race([p1, p2, p3]);

  • p1、p2、p3 都是 promise 对象,如果不是,调用 Promise.resolve 方法转换为 promise 对象
  • p 的状态由 p1、p2、p3 中状态最先变为 fulfilled 或 rejected 的 promise 对象决定
  • p 的终值或拒因由最先变更状态的 promise 对象所决定
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p2 error')
    },1000)
})
let p3 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p3 error')
    },500)
})
Promise.race([p1,p2,p3]).then(data=>{
    console.log(data);
}).catch(error=>{
    console.log(error);
})
// print: 1
let p1 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        resolve(1)
    },1000)
})
let p2 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p2 error')
    },800)
})
let p3 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p3 error')
    },500)
})

Promise.race([p1,p2,p3]).then(data=>{
    console.log(data);
}).catch(error=>{
    console.log(error);
})
// print: p3 error

Promise 的错误捕获

当 promise 的状态为 rejected 且为对 promise 对象使用 catch 方法,此时的异常信息会被 promise 对象吃掉 可以通过监听 unhandledRejection 事件,专门监听未捕获的reject错误

// node 环境下
process.on('unhandledRejection', error => {
    console.log('unhandledRejection', error);
});
// 浏览器下
window.addEventListener('unhandledrejection',(e)=>{
    e.preventDefault();
    console.log(e);
});

Promise 的问题

  • 无法取消Promise,若没有状态变更,也无法停止 promise 的等待
  • 不设定 then 或 catch 方法,构造函数(excutor函数)错误,无法捕获
  • 未完成状态时,无法得知是刚开始,还是即将完成

参考链接:https://juejin.im/post/5d06e9c76fb9a07ee4636235

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

推荐阅读更多精彩内容