ES6 规定,Promise对象是一个构造函数,用来生成Promise实例
下面是创建一个Promise实例
const promise = new Promise(function(resolve, reject) { if(/*异步操作成功*/){ resolve(value) } else{ reject(error) } })
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject. 它们是两个函数,由javascript引擎提供、
resolve 函数的作用是,将Promise对象的状态从“未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject 函数的作用是,将promise对象的状态从“未完成”变为"失败",在异步操作失败时调用。
Promise实例生成后,可以用then方法指定resolved状态和rejected状态的回调函数,
promise.then( function(value){ // success }, function(error){ // failure });
promise对象的简单的例子
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); });
上边代码中,timeout方法返回一个promise实例,表示一段时间后才会发生的结果,过了指定的时间(ms参数)以后,Promise实例的状态改变为resolved,就会触发then方法绑定的回调函数
promise新建后就会立即执行
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!'); // Promise // Hi! // resolved
上面代码中,Promise新建后立即执行,所以首先输出的Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
下面是异步加载图片的例子
function loadImageAsync(url) { return new Promise(function(resolve, reject) { const image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at ' + url)); }; image.src = url; }); }
下面是Ajax操作的例子
const getJSON = function(url) { const promise = new Promise(function(resolve, reject){ const handler = function() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; const client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
上边的代码中,getJSON是对XMLHttpRequest对象的封装
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数,reject函数的参数通常是 Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个Promise实例,
比如下面这样,
const p1 = new Promise(function (resolve, reject) { // ... }); const p2 = new Promise(function (resolve, reject) { // ... resolve(p1); })
上边代码中,p1 和 p2都是Promise的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作结果。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态,如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数就会立即执行。
若
const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) // Error: fail
上面代码中,p1是一个Promise,3秒之后变为rejected。p2的状态在1s之后发生改变,resolve方法返回的是p1。由于p2返回的是另一个Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态,所以,后面的then语句都变成了针对后者p1
注意,调用resolve或reject并不会终结Promise的参数函数的执行,
new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1
上面代码中,调用resolve(1)
以后,后面的console.log(2)
还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve
或reject
以后,Promise 的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。
new Promise((resolve, reject) => { return resolve(1); // 后面的语句不会执行 console.log(2); })