『ES6知识点总结』Promise

4次阅读

共计 11139 个字符,预计需要花费 28 分钟才能阅读完成。

『ES6 知识点总结』变量的解构赋值

本文主要内容如下:
1 Promise 是什么?
1.1 特点:
1.2 三种状态:
1.3 用处:
1.4 Promise 缺点:
1.5 历史过程
2 生成 promise 实例
3 promise 实例的 then() 方法
4 resolve 函数的参数可以是另一个 Promise 实例
5 Promise 的方法(多种)
5.1 Promise.all()
5.2 Promise.race()
5.3 Promise.reject(reason)
5.4 Promise.resolve(obj)
6 Promise.prototype.catch() 的概念
6.1 尽量不定义 then 的 reject 方法
6.2 catch 方法返回的还是一个 Promise 对象,因此后面还可以接着调用 then 方法。
7
7.1 如果前面的 promise 和 then() 没有报错,则会跳过 catch 方法。
7.2 抛错的冒泡性质
7.2.1 如果 Promise 状态已经变成 resolved,再抛出错误是无效的。
7.2.2 Promise 在下一轮“事件循环”再抛出错误
7.2.3 如果没有处理错误的回调函数,就没有反应
7.2.4 catch 方法之中,还能再抛出错误。
8 node.js 的 unhandledRejection
9 done()
10 finally()

Promise 是什么?
01、是一个对象,用来传递异步操作的消息。
02、代表了某个未来才会知道结果的事件(通常是一个异步操作)。
03、提供一个统一的 API,可以进一步处理,控制异步操作。

特点:

01、对象的状态不受外界影响。

02、代表一个异步操作。

三种状态:

Pending(进行中)Resolved(已完成,又称 Fulfilled)Rejected(已失败)。

(1)默认的 Promise 对象是等待态(Pending)。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
这也是 Promise 这个名字的由来,它的英语意思就是“许诺”,表示其他手段无法改变。

(2)一旦状态改变了,就不会再变了,会一直保持这个结果。
任何时候都可以得到这个结果。

Promise 对象的状态改变,只有两种:

从 Pending 变为 Resolved。从 Pending 变为 Rejected。

这与事件(Event)不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

用处:
用 Promise 对象可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
可以判断多个异步事件的完成,

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

历史过程

生成 promise 实例
Promise 是一个构造函数,可以生成 Promise 实例。

接受一个函数 A 作为参数,该函数 A 有 2 个参数,是 resolve 和 reject(拒绝),它们是函数,由 JS 引擎提供,不用自己部署。

resolve(value)函数的作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从 Pending 变为 Resolved),在异步操作成功时被调用,并将异步操作的结果,作为自己的参数。

reject(error)函数的作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从 Pending 变为 Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为自己的参数。

reject 函数的参数通常是 Error 对象的实例,表示抛出的错误。

例子:
reject(new Error(this.statusText));

在 promise 实例的构造函数中,可以只返回 resolve(value)或 reject(error);
例子:
let pro = new Promise((resolve,reject)=>{resolve(value)});

写法:
创造了一个 Promise 实例。

const promise = new Promise(function(resolve, reject) {if ( /* 异步操作成功 */) {resolve(value); } 
    else {reject(error); }
});

promise 实例的 then()方法
这个方法在实例的状态改变时会触发它的参数函数。
当 promise 实例转为成功 (Resolved) 状态时,触发 then()的第一个函数参数。
当 promise 实例转为失败 (Rejected) 状态时,触发 then()的第二个函数参数。(第二个函数参数可选)

这两个函数参数都接受 promise 实例传出的值作为参数。也就是 resolve(value)和 reject(value)的 value 值。

then()方法是定义在原型对象 Promise.prototype 上的,被所有 promise 实例继承。

promise.then(function (value) {// success},
    function (value) {// failure}
);

箭头函数方式:
promise.then((value)=>{}, (error)=>{})

如果 then()中有设置新的 promise 实例,则 then()方法返回这个 promise 对象。
否则将默认构建一个新的 promise 对象,并继承调用 then()方法的 promise 的状态(成功或失败)。

then 方法 return 返回的是一个新的 Promise 实例(注意,不是原来那个 Promise 实例)。
因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。

这个函数返回 undefined,则 then()方法构建一个默认的 Promise 对象,并且这个对象拥有 then()方法所属的 Promise 对象的状态。

var p = new Promise(function (resolve) {resolve();// 直接标志执行态
    }), temp;
temp = p.then(function () {// 传入执行态函数,不返回值});
 temp.then(function () {console.log('fulfilled');// 拥有 p 的状态
    });
console.log(temp === p);// 默认构建的 promise,但已经和 p 不是同一个对象,输出 false

如果对应状态所执行的函数返回一个全新的 Promise 对象,则会覆盖掉当前 Promise,代码如下:

var p = new Promise(function (resolve) {resolve();// 直接标志执行态
}), temp;
temp = p.then(function () {
    // 返回新的 promise 对象,和 p 的状态无关
    return new Promise(function (resolve, reject) {reject();// 标志拒绝态
    });
});
temp.then(function () {console.log('fulfilled');
}, function () {console.log('rejected');// 输出 rejected
});




resolve 函数的参数可以是另一个 Promise 实例
表示当前异步操作的取决于另一个异步操作的结果。
举个例子来说,张三到李四家拿钥匙,取决于李四在家不在家。
如果李四在家,那么张三可以成功拿到钥匙。如果李四不在家,张三就拿不到钥匙。

有 2 个 promise 实例 p1 和 p2,
p2 的 resolve(p1),那么,p1 的状态决定了 p2 的状态。
如果 p1 的状态是 Pending,那么 p2 的回调函数就会等待 p1 的状态改变,在此之前不会有结果。
如果 p1 的状态已经是 Resolved 或者 Rejected,那么 p2 的回调函数将会立刻执行。

比如像下面这样。

var p1 = new Promise(function(resolve, reject) {// ...});
var p2 = new Promise(function(resolve, reject) { // ...
    resolve(p1);
})

【】例子:
p1 是一个 Promise,3 秒之后变为 rejected。p2 的状态由 p1 决定,1 秒之后,p2 调用 resolve 方法,但是此时 p1 的状态还没有改变,因此 p2 的状态也不会变。又过了 2 秒,p1 变为 rejected,p2 也跟着变为 rejected。


var p1 = new Promise(function(resolve, reject) {setTimeout(() => reject(new Error('fail')), 3000);
});

var p2 = new Promise(function(resolve, reject) {setTimeout(() => resolve(p1), 1000)
});
p2.then(result => console.log(result))
p2.catch(error => console.log(error))
    // Error: fail

【】例子:
下面是一个 Promise 对象的简单例子。
timeout 函数中,会返回新生成的 Promise 实例,其中,定时器 setTimeout 会在 100ms 后调用 resolve(“done”);当调用 resolve 后,触发 then 绑定的回调函数。
zyx456:’done’ 是 resolve 的参数。
等效于:

function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve("done"),ms);
    });
}
timeout(100).then((value) => {console.log(value);
});




【】例子:
下面是异步加载图片的例子。

function loadImageAsync(url) {

return new Promise(function(resolve, reject) {var image = new Image();
    image.onload = function() { resolve(image); };
    image.onerror = function() { reject(new Error('Could not load image at' + url)); };
    image.src = url;
});

}

【】例子:
下面是一个用 Promise 对象实现的 Ajax 操作的例子。
getJSON 是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个 Promise 对象。

var getJSON = function (url) {var promise = new Promise(function (resolve, reject) {var client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

    function handler() {if (this.readyState !== 4) {return;}
        if (this.status === 200) {resolve(this.response);
        } else {reject(new Error(this.statusText));
        }
    };
});
return promise;
};
getJSON("/posts.json").then(function (json) {console.log('Contents:' + json);
}, function (error) {console.error('出错了', error);
});

箭头函数写法:

getJSON("/post/1.json").then(post => getJSON(post.commentURL)).then(comments => console.log("Resolved:", comments),
err => console.log("Rejected:", err));

Promise 的方法(多种)

Promise.all()

【01】用于将多个 Promise 实例,包装成一个新的 Promise 实例。
接受一个数组作为参数,数组元素都是 Promise 实例。
如果参数不是 Promise 实例,会调用 Promise.resolve()转变为 Promise 实例。

Promise.all()方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。

【】返回的新的 Promise 实例的状态:

所有 Promise 参数的都变成 Resolved,p 的状态才会变成 Resolved,参数的返回值组成一个数组,传递给 p 的回调函数。

只要有一个参数被 rejected,p 的状态就变成 rejected,此时第一个 reject 的实例的返回值,会传递给 p 的回调函数。

(小 z:类似于且操作,且假或真,只有所有的是成功的才可以。你想想,一系列的操作只有都成功才叫成功。)

var p = Promise.all([p1,p2,p3]);

例子

// 生成一个 Promise 对象的数组
var promises = [2, 3, 5, 7, 8].map(function(id){return getJSON("/post/" + id + ".json");});

Promise.all(promises).then(function(posts) {// ...}).catch(function(reason){// ...});

Promise.race()
race:赛跑。
【01】将多个 Promise 实例,包装成一个新的 Promise 实例。
返回这个新 promise 实例,新 promise 实例的状态为第一个改变的参数实例的状态。它的值也是该参数实例传递出来的值。

03,参数如果不是 Promise 实例,就会调用 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。

var p = Promise.race([p1,p2,p3]);

例子:

var p = Promise.race([fetch('/resource-that-may-take-a-while'),
    new Promise(function (resolve, reject) {setTimeout(() => reject(new Error('request timeout')), 5000)
    })])
p.then(response => console.log(response))
p.catch(error => console.log(error))


Promise.reject(reason)

【】会返回一个新的 Promise 实例,该实例的状态为 rejected。
参数 reason,会被传递给实例的回调函数。


var p = Promise.reject('出错了');
// 等同于
var p = new Promise((resolve, reject) => reject('foo'))

p.then(null, function (s){console.log(s)});// 出错了




Promise.resolve(obj)

【01】将对象转为 Promise 对象。
如果参数不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为 Resolved。
比如:参数为字符串时,不属于异步操作(判断方法是它不是具有 then 方法的对象),所以等价于 Promise 构造函数立即执行 resolve(“str”);

03,如果没有参数,可以得到一个 Promise 对象。
04,如果参数是一个 Promise 实例,则会被原封不动地返回。

var jsPromise = Promise.resolve($.ajax(‘/whatever.json’));

Promise.resolve 等价于下面的写法。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

例子:

    var p = Promise.resolve('Hello');
p.then(function (s){console.log(s)});// Hello

例子:

var p = Promise.resolve();
p.then(function () {// ...});

Promise.prototype.catch()的概念
【01】是 promise 实例.then(null, rejection) 的别名,用于指定发生错误时的回调函数。

【】例子:
getJSON 方法返回一个 Promise 对象,如果该对象状态变为 Resolved,则会调用 then 方法指定的回调函数;
如果异步操作抛出错误,状态就会变为 Rejected,就会调用 catch 方法指定的回调函数,处理这个错误。

getJSON("/posts.json").then(function(posts) {// ...}).catch(function(error) { // 处理前一个回调函数运行时发生的错误
    console.log('发生错误!', error);
});

例子:

p.then((val) => console.log("fulfilled:", val)).catch((err) => console.log("rejected:", err));
// 等同于

p.then((val) => console.log(fulfilled: ", val)).then(null, (err) => console.log("
 rejected: ", err));

例子:
Promise 抛出一个错误,就被 catch 方法指定的回调函数捕获。


var promise = new Promise(function(resolve, reject) {throw new Error('test') });
promise.catch(function(error) {console.log(error) });// Error: test

尽量不定义 then 的 reject 方法
一般来说,不要在 then 方法里面定义 Reject 状态的回调函数(即 then 的第二个参数),总是使用 catch 方法。
第二种写法要好于第一种写法,理由是前者更接近同步的写法(try/catch)。

// bad
promise
    .then(function(data) {// success}, function(err) {// error});
// good
promise
    .then(function(data) { //cb
        // success
    }).catch(function(err) {// error});


catch 方法返回的还是一个 Promise 对象,因此后面还可以接着调用 then 方法。

var someAsyncThing = function() {return new Promise(function(resolve, reject) { // 下面一行会报错,因为 x 没有声明
    resolve(x + 2);
});
};
someAsyncThing().catch(function(error) {console.log('oh no', error);
}).then(function() {console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on

如果前面的 promise 和 then()没有报错,则会跳过 catch 方法。

Promise.resolve().catch(function(error) {console.log('oh no', error);
}).then(function() {console.log('carry on');
});
// carry on


抛错的冒泡性质
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。
也就是说,错误总是会被下一个 catch 语句捕获。

例子:
一共有三个 Promise 对象:一个由 getJSON 产生,两个由 then 产生。它们之中任何一个抛出的错误,都会被最后一个 catch 捕获。

getJSON(“/post/1.json”).then(function(post) {

return getJSON(post.commentURL); }).then(function(comments) {// some code

}).catch(function(error) {// 处理前面三个 Promise 产生的错误
});

如果 Promise 状态已经变成 resolved,再抛出错误是无效的。
Promise 在 resolve 语句后面,再抛出错误,不会被捕获,等于没有抛出。

var promise = new Promise(function(resolve, reject) {
resolve(“ok”);

throw new Error('test'); });

promise.then(function(value) {console.log(value) }).catch(function(error) {console.log(error) });// ok

Promise 在下一轮“事件循环”再抛出错误
结果由于没有指定使用 try…catch 语句,就冒泡到最外层,成了未捕获的错误。
因为此时,Promise 的函数体已经运行结束了,所以这个错误是在 Promise 函数体外抛出的。

var promise = new Promise(function(resolve, reject) {resolve("ok");
    setTimeout(function() {throw new Error('test') }, 0) });
promise.then(function(value) {console.log(value) });
// ok
// Uncaught Error: test


如果没有处理错误的回调函数,就没有反应
跟传统的 try/catch 代码块不同的是,如果没有使用 catch 方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

someAsyncThing 函数产生的 Promise 对象会报错,但是由于没有指定 catch 方法,这个错误不会被捕获,也不会传递到外层代码,导致运行后没有任何输出。

var someAsyncThing = function() {return new Promise(function(resolve, reject) { // 下面一行会报错,因为 x 没有声明
        resolve(x + 2);
    });
};
someAsyncThing().then(function() {console.log('everything is great');
});



catch 方法之中,还能再抛出错误。
catch 方法抛出一个错误,因为后面没有别的 catch 方法了,导致这个错误不会被捕获,也不会传递到外层。

var someAsyncThing = function() {return new Promise(function(resolve, reject) { // 下面一行会报错,因为 x 没有声明
        resolve(x + 2);
    });
};
someAsyncThing().then(function() {return someOtherAsyncThing();
 }).catch(function(error) {console.log('oh no', error); // 下面一行会报错,因为 y 没有声明
    y + 2;
}).then(function() {console.log('carry on');
});
// oh no [ReferenceError: x is not defined]


如果改写一下,结果就不一样了。
第二个 catch 方法用来捕获,前一个 catch 方法抛出的错误。

someAsyncThing().then(function() {return someOtherAsyncThing(); }).catch(function(error) {console.log('oh no', error); // 下面一行会报错,因为 y 没有声明
    y + 2;
}).catch(function(error) {console.log('carry on', error);
});
// oh no [ReferenceError: x is not defined]
// carry on [ReferenceError: y is not defined]



node.js 的 unhandledRejection
Node.js 有一个 unhandledRejection 事件,专门监听未捕获的 reject 错误。
unhandledRejection 事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。


process.on('unhandledRejection', function (err, p) {console.error(err.stack)});




done()

Promise 对象的回调链,不管以 then 方法或 catch 方法结尾,要是最后一个方法抛出错误,都有可能会漏掉(Promise 内部的错误不会冒泡到全局)。
因此,我们可以提供一个 done 方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。

asyncFunc().then(f1).catch(r1).then(f2).done();

done 方法的使用,可以像 then 方法那样用,提供 Fulfilled 和 Rejected 状态的回调函数,也可以不提供任何参数。但不管怎样,done 都会捕捉到任何可能出现的错误,并向全局抛出。

实现代码:

Promise.prototype.done = function (resolve, reject) {this.then(resolve, reject).catch(function (reason) { // 抛出一个全局错误
        setTimeout(() => {throw reason}, 0);
    });
};




finally()
finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
它与 done 方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

等效于,运行一个 then(),在成功和失败回调函数中,都要运行 callback(),
在 Promise.resolve()中运行 callback()后,在运行 then(),返回结果。

下面是一个例子,服务器使用 Promise 处理请求,然后使用 finally 方法关掉服务器。

server.listen(0).then(function () {// run test}).finally(server.stop);

它的实现:

Promise.prototype.finally = function(callback) {
    return this.then(value => Promise.resolve(  callback()  ).then(() => value),
        reason => Promise.resolve(callback()).then(() => {throw reason}));
};







正文完
 0