一、回调地狱
在没有Promise之前,如果我们想要操作多个异步函数,会有回调地狱
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
同时这种写法还有另外两个问题:
- 回调函数命名缺乏规范, 每个程序员都有可能给回调函数起不同的名字
- 不能很好地捕获错误
二、初探Promise
Promise可以完美解决以上三个问题,即
- 解决回调地狱,增强代码可读性
- 规范回调的名字或顺序
- 很方便地捕获错误
而有了Promise后, 相同的操作可以写成:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
以上的回调函数还可以简化为箭头函数
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
三、怎么写一个Promise
promise对象是由window.Promise构造函数得到的一个对象
从0开始新建一个promise对象:
Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数接受两个函数——resolve 和 reject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。
resolve函数和reject函数都只接受一个参数
const myFirstPromise = new Promise((resolve, reject) => {
// ?做一些异步操作,最终会调用下面两者之一:
//
// resolve(someValue); // fulfilled
// ?或
// reject("failure reason"); // rejected
});
想让一个函数有Promise对象的功能:
return new Promose( (resolve, reject) => { )
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText); // 成功就调用resolve函数
xhr.onerror = () => reject(xhr.statusText); // 失败就调用reject函数
xhr.send();
});
};
基础示例
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
}, function(errorMessage){
// reject会调用这个函数
});
易混淆点:resolve 和 reject 和 then(succes, fail)
new Promise(function(resolve, reject){}里的resolve 和 reject 并不是 .then(succes, fail) 里面的 success 和 fail,
resolve 会去调用 success,reject 会去调用 fail
AJAX+Promise请求图片示例
function imgLoad(url) {
// Create new promise with the Promise() constructor;
// This has as its argument a function
// with two parameters, resolve and reject
return new Promise(function(resolve, reject) {
// Standard XHR to load an image
let request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'blob';
// When the request loads, check whether it was successful
request.onload = function() {
if (request.status === 200) {
// If successful, resolve the promise by passing back the request response
resolve.call(null, request.response);
} else {
// If it fails, reject the promise with a error message
reject.call(null, Error('Image didn\'t load successfully; error code:' + request.statusText));
}
};
request.onerror = function() {
// Also deal with the case when the entire request fails to begin with
// This is probably a network error, so reject the promise with an appropriate message
reject.call(null, Error('There was a network error.'));
};
// Send the request
request.send();
});
}
// Get a reference to the body element, and create a new image object
var body = document.querySelector('body');
var myImage = new Image();
// Call the function with the URL we want to load, but then chain the
// promise then() method on to the end of it. This contains two callbacks
imgLoad('myLittleVader.jpg').then(function(response) {
// The first runs when the promise resolves, with the request.response
// specified within the resolve() method.
var imageURL = window.URL.createObjectURL(response);
myImage.src = imageURL;
body.appendChild(myImage);
// The second runs when the promise
// is rejected, and logs the Error specified with the reject() method.
}, function(Error) {
console.log(Error);
});
四、Promise使用方法小结
第一步
- return new Promise((resolve, reject) =>{...})
- 任务成功就调用 resolve.call(null, result)
- 任务失败就调用 reject.call(null, errorMessage)
- resolve和reject会再去调用成功和失败函数
第二步
- 使用.then(success, fail) 传入成功和失败函数
- success.call(null, result) 参数来自resolve
- fail.call(null, errorMessage) 参数来自reject