Promise讲解及应用(并行调用、串行调用及使用Array.reduce进行链式调用)

Promise 是什么

ECMAscript 6 原生提供了 Promise 对象。MDN上关于Promise是这么解释的:

Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。

Promise 状态

一个Promise通常有以下几种状态

  • pending(待定):初始状态,既不是成功也不是失败。
  • fulfilled(已兑现、成功):操作成功。
  • rejected(已拒绝、失败):操作失败。

待定pending状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。

链式调用

对于一个Promise,常用的做法是将 Promise.then()Promise.catch()Promise.finally()这些方法将进一步的操作与一个变为已敲定状态的 Promise关联起来。
Promise讲解及应用(并行调用、串行调用及使用Array.reduce进行链式调用)_第1张图片

以以下代码举例:

const p = new Promise((resolve, reject) => {
	setTimeout(() => {
		console.log('operate in promise');
		// resolve将Promise状态标记为fulfilled,此后执行Promise.then中的回调函数,其中参数1可以被then中的回调函数接收
		resolve(1);
		// reject方法将Promise状态标记为rejected
		// reject(new Error(someError))
	}, 1000);
});

p.then((result) => {
    // Promise状态变为rejected后,执行异常处理回调函数
    // 其中参数result为上面resolve方法的参数:1
	console.log('promise fulfilled, return value: ', result);
}).catch((err) => {
    // Promise状态变为rejected后,执行异常处理回调函数
    // 其中err为reject方法的参数
	console.log('promise rejected', err);
}).finally(() => {
    // 成功或异常处理完成后,执行finally中的回调函数
	console.log('finally.')
});

上面的例子中,执行结果如下:在这里插入图片描述
通常的做法是上例中的形式,即p.then(() => {}).catch(() => {}).finally(() => {}),不过查阅MDN文档发现,链式调用除了上面的形式,还有这样的形式,即将成功处理和失败处理同放于then的回调函数中,如下:

p.then((result) => {
  // handle fulfilled
}, (err) => {
  // handle rejected
})

这种形式在写法上和上面的形式有点区别,但是作用是一样的,就不展开描述。

多个Promise调用

多个Promise并发调用

多个Promise并发执行可以借助Promise.all()方法执行,该方法返回一个 Promise 实例。方法定义如下:

Promise.all<T>(values: Iterable<T | PromiseLike<T>>): Promise<T[]>;

是待多个Promise全部返回成功状态时,才将Promise状态标记为成功fullfilled,只要有任何一个Promise返回失败状态,状态则被标记为失败rejected,在可以在MDN查看Promise.all()方法的解释:Promise.all()


        const p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log(11);
                resolve(11);
            }, 1000)
        })
        const p2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log(22);
                resolve(22);
            }, 1000)
        })
        const p3 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log(33);
                resolve(44);
            }, 1000)
        })

        Promise.all([p1, p2, p3]).then(results => {
            // p1,p2,p3中全部成功后执行
            console.log('all resolved: ', results)
        }).catch((err) => {
            // p1,p2,p3中只要有一个失败,执行catch方法
            console.log('promise rejected', err);
        });

执行结果如下:执行结果

多个Promise串行调用

对于多个Promise实例对象p1、p2、p3,如果需要串行执行,即先执行p1,成功后执行p2,再成功后执行p3,主要借助于Promise.then()。实例代码:


        const p1 = (preResult) => {
            // preResult为前一个Promise返回值,由于p1之前没有Promise,此处为undefined
            return new Promise((resolve, reject) => {
                console.log('pre return value: ', preResult);
                resolve(11)
            });
        }
        const p2 = (preResult) => {
            // preResult为前一个Promise返回值,此处即p1的返回值11
            return new Promise((resolve, reject) => {
                console.log('pre return value: ', preResult);
                resolve(22)
            });
        }
        const p3 = (preResult) => {
            // preResult为前一个Promise返回值,此处即p2的返回值22
            return new Promise((resolve, reject) => {
                console.log('pre return value: ', preResult);
                resolve(33)
            });
        }

        p1().then(p2).then(p3).then((result) => {
            // result为链式调用中最后一个Promise的返回值,此处为p3返回值33
            console.log('last return value: ', result);
        })

Promise讲解及应用(并行调用、串行调用及使用Array.reduce进行链式调用)_第2张图片

不确定数目的Promise的串行调用

对于上面两个例子中,Promise数量都是固定的,调用起来非常方便,当然对于Promise.all方式调用,可以调用任意个数量的Promise,只要传进来的参数是个数组就可以。但是对于串行调用,即p1().then(p2).then(p3).then((result) => {})的形式,如果Promise数量不是固定的,就无法通过枚举的形式一个一个列出来加到then中进行调用。
此时我们可以借助于Array.reduce()进行串行调用,Array.reduce()方法通常用于数组的累加求和,方法定义如下:

interface Array<T> {
	/**
     * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
     * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
     * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
     */
	reduce(callbackfn: (previousValue: T, currentValue: T,      currentIndex: number, array: T[]) => T, initialValue: T): T;
}

其中第一个参数callbackfn为回调函数,第二个参数initialValue非必须,为初始值。Array.reduce 可以处理数组中的每一个元素,并将处理后的结果作为参数传递给下一个处理函数。这样,我们可以在callbackfn中构造一个Promise,并传递给下一个处理函数。


        const arr = [];

        const p1 = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(new Date());
                    resolve();
                }, 1000)
            })
        }
        arr.push(p1);
        const p2 = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(new Date());
                    resolve();
                    // resolve();
                }, 1000)
            })
        }
        arr.push(p2);
        const p3 = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(new Date());
                    resolve();
                }, 1000)
            })
        }
        arr.push(p3);

        // 可以在数组arr中添加其他Promise...

        arr.reduce((prev, curv) => {
            return prev.then(() => {
                return curv().then()
            })
        }, Promise.resolve()).then(() => {
            console.log('all resolved');
        }).catch(err => {
            console.log('error');
            console.error(err);
        });

执行结果如下:Promise讲解及应用(并行调用、串行调用及使用Array.reduce进行链式调用)_第3张图片
可以看到上面的例子中,多个Promise依次串行执行,每个Promise在上个Promise执行完成后再执行。
当然实际上Promise串行执行的引用远远不止这么简单,在此不展开来讲解。

你可能感兴趣的:(js,前端,js)