基本概念

做异步请求时,我们往往会在请求成功的回调函数里继续写函数,或者继续发送异步,继续回调,回调函数里又回调,一层一层嵌套,就会形成回调地狱。这会使我们的代码可读性变差,不好维护,性能也下降,promise可以解决回调地狱的问题

Promise起到代理的作用,被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法,这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象

Promise对象用于表示一个异步操作的最终状态(完成或失败),以及该异步操作的结果值

一个Promise有以下几种状态:

  • pending: 初始状态,既不是成功,也不是失败状态

  • fulfilled: 意味着操作成功完成

  • rejected: 意味着操作失败

只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

Promise对象只可能会发生两种改变:

  • pending状态变为fulfilled状态并传递一个值给相应的状态处理方法

  • pending状态变为rejected并传递失败信息

当其中任一种情况出现时,Promise对象的then方法绑定的处理方法就会被调用,then方法包含两个函数作为参数:onfulfilledonrejected

Promise状态为fulfilled时,调用onfulfilled方法,反之调用onrejected方法

ES6-Promise

基本用法

Promise对象是一个构造函数,用来生成Promise实例

1
2
3
4
5
6
7
8
let promise = new Promise((resolve, reject) => {
// code ...
if (/* success */) {
resolve(value);
} else {
reject(error);
}
});

可以看到,Promise构造函数接受一个函数作为参数,而该函数的两个参数分别为resolve函数和reject函数

  • 当异步操作成功,也即是从pending状态变为fulfilled状态时,调用resolve,并将异步操作的结果作为参数传递出去

  • 当异步操作失败,也即是从pending状态变为rejected状态时,调用reject,并将异步操作的结果作为参数传递出去

Promise实例生成以后,可使用then方法分别指定fulfilledrejected状态的回调函数

1
2
3
4
5
promise.then(value => {
// success ...
}, error => {
// failure ...
})

来看一个例子

1
2
3
4
5
6
7
8
9
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}

timeout(2000).then(value => {
show(value);
});

timeout方法返回一个Promise实例,表示过一段时间后执行,当Promise状态变为fulfilled时就会触发then绑定的回调函数

Promise在新建后会立刻执行,then方法指定的回调函数会在当前脚本所有同步任务执行完之后才会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
});

promise.then(() => {
console.log(2);
});

console.log(3);

// 1
// 3
// 2

当异步操作失败,调用reject,其参数通常是Error对象的实例

1
2
3
4
5
6
7
8
9
10
11
function loadImageAsync(url) {
return new Promise((resolve, reject) => {
let image = new Image();
image.onload = () => resolve(image);
image.onerror = () => reject(new Error(`Could not load image at ${url}`));
image.src = url;
});
}

loadImageAsync('hello');
// Uncaught (in promise) Error: Could not load image at hello at Image.image.onerror

Promise对象也可以作为参数被传入resolve

1
2
3
4
5
6
7
8
9
10
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('fail')), 3000);
});

let promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(promise1), 1000);
});

promise2.then(result => console.log(result))
.catch(error => console.log(error));

promise1的状态在3秒后改变,promise2的状态在1秒后改变,由于promise2返回的是promise1,因此1秒后promise2的状态由promise1决定,故在两秒后触发catch

调用resolve或者reject并不会终止Promise的执行

1
2
3
4
5
6
7
8
new Promise((resolve, reject) => {
resolve('hello');
console.log('world');
}).then(res => {
console.log(res);
});
// world
// hello

一般在resolve或者reject前加return

1
2
3
4
5
6
7
new Promise((resolve, reject) => {
return resolve('hello');
console.log('world');
}).then(res => {
console.log(res);
});
// hello

实例方法

Promise.prototype.then()

该方法可以为Promise实例添加状态改变时的回调函数

1
2
3
4
5
6
7
Promise.prototype.then(onFulfilled[, onRejected])

promise.then(value => {
// fulfilled code ...
}, err => {
// rejected code ...
})
  • onFulfilled: 第一个参数是fulfilled状态的回调函数

  • onRejected: 第二个参数是rejected状态的回调函数,可选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function f(flag) {
return new Promise((resolve, reject) => {
if (flag) {
resolve('success');
} else {
reject(new Error('Error!'))
}
}).then(res => {
console.log(res);
}, err => {
console.error(err);
});
}

f(0) // => error
f(1) // => success

当一个Promise成功或者失败,具体的返回值依据以下规则返回:

  • 如果then中的回调函数返回一个值,那么then返回的Promise将会成为fulfilled状态,并且将返回的值作为fulfilled状态的回调函数的参数值

  • 如果then中的回调函数没有返回值,那么then返回的Promise将会成为fulfilled状态,并且该fulfilled状态的回调函数的参数值为undefined

  • 如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为rejected状态,并且将抛出的错误作为rejected状态的回调函数的参数值

  • 如果then中的回调函数返回一个已经是fulfilled状态的Promise,那么then返回的Promise也会成为fulfilled状态,并且将那个Promisefulfilled状态的回调函数的参数值作为该被返回的Promisefulfilled状态回调函数的参数值

  • 如果then中的回调函数返回一个已经是rejected状态的Promise,那么then返回的Promise也会成为rejected状态,并且将那个Promiserejected状态的回调函数的参数值作为该被返回的Promiserejected状态回调函数的参数值

  • 如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的

举例来说

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let promise = Promise.resolve(123).then(value => {
console.log(value);
return value;
})

console.log(promise)
debugger;

setTimeout(() => {
console.log(promise)
})

// Promise {<pending>}: [[PromiseStatus]]: "pending" [[PromiseValue]]: undefined
// 123
// Promise {<resolved>: 123}: [[PromiseStatus]]: "resolved" [[PromiseValue]]: 123

then方法返回一个新的Promise对象,故可以使用链式操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Promise.resolve('a').then(str => {
return new Promise((resolve, reject) => setTimeout(() => {
str += 'b';
resolve(str);
}, 1000));
}).then(str => {
setTimeout(() => {
str += 'c';
console.log(str);
}, 1000);
return str;
}).then(str => {
console.log('hello');
console.log(str);
});

// hello
// ab
// abc

Promise.prototype.catch()

该方法用于指定发生错误时的回调函数,相当于.then(null, onRejected)

1
2
3
4
5
Promise.prototype.catch(onRejected);

promise.catch(function(reason) {
// rejected code
});
  • onRejected

    Promiserejected时,被调用的一个函数

1
2
3
4
5
6
var promise = new Promise((resolve, reject) => {
throw new Error('catch test');
}).catch(error => {
console.log(error);
});
// Error: catch test

等效于

1
2
3
4
5
6
var promise = new Promise((resolve, reject) => {
throw new Error('catch test');
}).then(null, error => {
console.log(error);
});
// Error: catch test

如果Promise状态已经变成Resolved,则再抛出错误无效

1
2
3
4
5
6
7
var promise = new Promise((resolve, reject) => {
resolve('success');
throw new Error('error after resolve');
}).catch(error => {
console.log(error);
});
// (无输出)

Promise对象的错误可以一直向后传递,直到被捕获

1
2
3
4
5
6
7
8
9
10
11
var promise = new Promise((resolve, reject) => {
throw new Error('top error');
}).then(value => {
console.log(value);
}).then(value => {
console.log(value);
}).catch(error => {
console.log(error);
});
// (then 中无输出)
// Error: top error

Promise.prototype.finally()

finally用于指定不管Promise对象最后状态如何都会执行的操作,接受一个

静态方法

Promise.resolve()

Promise.resolve()可以将现有对象转为Promise对象

1
2
3
4
Promise.resolve('hello').then(value => {
console.log(value);
});
// hello

上面的写法等价于

1
2
3
4
new Promise(resolve => resolve('hello')).then(value => {
console.log(value);
});
// hello

Promise.resolve可以接受四种参数

  • Promise 实例

    如果参数是Promise实例,那么该方法不会对其做任何修改,直接返回这个实例

    1
    2
    3
    4
    5
    6
    var p1 = new Promise(resolve => resolve('hello'));

    Promise.resolve(p1).then(value => {
    console.log(value + ' world');
    })
    // hello world
  • thenable 对象

    thenable对象是指具有then方法的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let thenable = {
    then: function (resolve, reject) {
    resolve('hello');
    }
    };

    Promise.resolve(thenable).then(value => {
    console.log(value);
    });
    // hello
  • 不具有 then 方法的对象或者原始值

    Promise.resolve返回一个新的Promise对象,状态为fulfilled,参数将作为回调函数的参数值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let obj = {
    a: 'hello',
    b: 'world'
    };

    Promise.resolve(obj).then(value => {
    console.log(value.a + value.b);
    });
    // helloworld
  • 无参数

    直接返回一个fulfilled状态的对象

Promise.reject()

Promise.reject方法返回一个Promise对象,其状态为rejected

1
2
3
4
Promise.reject(new Error('Error')).catch(error => {
console.log(error);
});
// Error: Error

Promise.all()

Promise.all用于将多个Promise实例包装成一个新的Promise实例

1
Promise.all(iterable);

iterable是一个可遍历的对象

  • 如果传入的参数是一个空的对象或不包含任何Promise对象,则返回一个fullfilled状态的Promise对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Promise.all([]).then(resolve => {
    console.log('resolved');
    }, error => {
    console.log('rejected');
    });
    // resolved
    --------------------------------------------------------
    Promise.all([1, 2, 3]).then(resolve => {
    console.log('resolved');
    }, error => {
    console.log('rejected');
    });
    // resolved
  • 其它情况下,若传入的Promise状态都变成fulfilled,则返回一个fulfilled状态的Promise,各个实例的返回值组成一个数组,作为回调函数的参数;只要一个Promise状态为rejected,则返回rejected,第一个被rejected的实例的返回值作为回调函数的参数

    1
    2
    3
    4
    5
    6
    7
    8
    var p1 = Promise.resolve('hello'),
    p2 = Promise.resolve(' '),
    p3 = Promise.resolve('world');

    Promise.all([p1, p2, p3]).then(value => {
    console.log(value.join(''));
    });
    // hello world
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var p1 = Promise.resolve('hello'),
    p2 = Promise.reject(new Error('error')),
    p3 = Promise.resolve('world');

    Promise.all([p1, p2, p3]).then(value => {
    console.log(value.join(''));
    }, error => {
    console.log(error);
    });
    // Error: error

Promise.race()

Promise.all同样用于将多个Promise实例包装成一个新的Promise实例,与Promise.all不同的是,返回的Promise的状态取决于最先改变的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'hello');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'world');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(reject, 200, new Error('error'));
});

Promise.race([p1, p2, p3]).then(value => {
console.log(value);
}, error => {
console.log(error);
});
// world

如果将上面p3的时间改为20,则会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'hello');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'world');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(reject, 20, new Error('error'));
});

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

Promise.allSettled()

Promise.all类似,用于将多个Promise实例包装成一个新的Promise实例,但不同之处在于会返回每个实例的状态

1
2
3
4
5
6
7
8
9
10
var p1 = Promise.resolve('hello'),
p2 = Promise.reject(new Error('error')),
p3 = Promise.resolve('world');

Promise.allSettled([p1, p2, p3]).then(value => {
console.log(value);
});
// hello
// Error: error
// world