所有异步编程的根基都是回调函数
回调函数可以理解成一件你想要做的事情。
也就是说,由调用者定义,交给执行者执行的函数就叫做回调函数
Promise 一种更优的异步编程统一方案
如果我们使用传统的回调方式去完成复杂的异步流程,就会无法避免的遭遇一个传统的问题:回调地狱。CommonJS为了避免这个问题,提供了一个Promise方案。
Promise基本用法
Promise实例:
// Promise 基本示例
const promise = new Promise(function (resolve, reject) {
// 这里用于“兑现”承诺
// resolve(100) // 承诺达成
reject(new Error("promise rejected")); // 承诺失败
});
promise.then(
function (value) {
// 即便没有异步操作,then 方法中传入的回调仍然会被放入队列,等待下一轮执行
console.log("resolved", value);
},
function (error) {
console.log("rejected", error);
}
);
console.log("end");
Promise使用案例(ajax by Promise)
// Promise 方式的 AJAX
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
// 请求完成之后直接拿到JSON文件
xhr.responseType = "json";
xhr.onload = function () {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
xhr.send();
});
}
// 提前准备好的JSON文件
ajax("/api/users.json").then(
function (res) {
console.log(res);
},
function (error) {
console.log(error);
}
);
Promise常见的误区
嵌套使用的方式是Promise最常见的误区
错误例:
// Promise 常见误区
function ajax (url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function () {
if (this.status === 200) {
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
// 嵌套使用 Promise 是最常见的误区
// ajax('/api/urls.json').then(function (urls) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// })
// })
// })
// })
// })
正确例(链式调用)
// Promise 链式调用
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "json";
xhr.onload = function () {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
xhr.send();
});
}
// then 方法返回的也是一个Promise对象
// var promise = ajax('/api/users.json')
// var promise2 = promise.then(
// function onFulfilled (value) {
// console.log('onFulfilled', value)
// },
// function onRejected (error) {
// console.log('onRejected', error)
// }
// )
// console.log(promise2 === promise)
// false (返回的是一个全新的Promise对象)
//目的是实现一个Promise的链条
ajax("/api/users.json")
.then(function (value) {
console.log(1111);
return ajax("/api/urls.json");
}) // => Promise
.then(function (value) {
console.log(2222);
console.log(value);
return ajax("/api/urls.json");
}) // => Promise
.then(function (value) {
console.log(3333);
return ajax("/api/urls.json");
}) // => Promise
.then(function (value) {
console.log(4444);
return "foo";
}) // => Promise
.then(function (value) {
console.log(5555);
console.log(value);
});
总结:
1.Promise的then方法可以返回一个全新的Promise对象,我们可以使用链式调用的方法添加then方法
2.后面的then方法实际上就是为上一个then方法返回的Promise注册回调
3.前面then回调函数的返回值会作为后面then方法回调的参数
4.如果我们前面回调返回的是一个Promise的话,后面的then方法回调会等待这个Promise结束
Promise错误捕获
有四种种写法:
then的第二个参数:
// 同时注册的 onRejected 只是给当前 Promise 对象注册的失败回调
// 它只能捕获到当前 Promise 对象的异常
ajax('/api/users.json')
.then(function onFulfilled (value) {
console.log('onFulfilled', value)
return ajax('/error-url')
}, function onRejected (error) {
console.log('onRejected', error)
})
链接调用的catch() 更加通用
// 因为 Promise 链条上的任何一个异常都会被一直向后传递,直至被捕获
// 分开注册的 onRejected 相当于给整个 Promise 链条注册失败回调
ajax("/api/users.json")
.then(function onFulfilled(value) {
console.log("onFulfilled", value);
return ajax("/error-url");
}) // => Promise {}
.catch(function onRejected(error) {
console.log("onRejected", error);
});
全局捕获Promise异常,类似于window.onerror
window.addEventListener(
"unhandledrejection",
(event) => {
const { reason, promise } = event;
console.log(reason, promise);
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的 Promise 对象
event.preventDefault();
},
false
);
Nodejs全局捕获异常
// Node.js 中使用以下方式
process.on('unhandledRejection', (reason, promise) => {
console.log(reason, promise)
// reason => Promise 失败原因,一般是一个错误对象
// promise => 出现异常的 Promise 对象
})
全局捕获的方式并不是一件值得推崇的事,更好的做法是在代码中明确的捕获每一个可能发生的异常
Promise静态写法
Promise.resolve(value)
把一个值快速的转变成一个Promise对象
Promise.resolve('foo')
.then(function (value) {
console.log(value)
})
这段代码基本等价于:
new Promise(function (resolve, reject) {
resolve('foo')
})
值得注意的是:如果使用这个方法包装一个Promise,那么将会原路返回这个Promise对象
// 如果传入的是一个 Promise 对象,Promise.resolve 方法原样返回
var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) // true
有一个特殊情况:
// 如果传入的是带有一个跟 Promise 一样的 then 方法的对象,
// Promise.resolve 会将这个对象作为 Promise 执行
Promise.resolve({
then: function (onFulfilled, onRejected) {
onFulfilled('foo')
}
})
.then(function (value) {
console.log(value) // foo
})
可以说是实现了thenable的接口,可以被then的对象,支持这个的原因在于,在Promise原生对象没有普及之前,很多的时间我们都是使用的第三方库实现的Promise,在现在的环境下,要将这种三方的Promise转为原生的Promise就可以用到这个方法。
Promise.reject()
// Promise.reject 传入任何值,都会作为这个 Promise 失败的理由
// Promise.reject(new Error('rejected'))
// .catch(function (error) {
// console.log(error)
// })
Promise.reject("anything").catch(function (error) {
console.log(error);
});