Promise 基础

Promise 是为异步操作的结果所准备的占位符。是解决异步编程的一种方法,是规避回调地狱(callbacK hell)的一种解决方案

Promise 的生命周期

  • 创建时是一个未决状态,白话文就是还没有决定结果
  • 已完成( fulfilled ): Promise 的异步操作已成功结束;
  • 已拒绝( rejected ): Promise 的异步操作未成功结束,可能是一个错误,或由其他原
    因导致。

Promise的方法

promise有以下方法:

  • then()
  • catch()
  • all()
  • race()

then()方法

promise.then()接受两个参数。第一个参数是Promise被完成时要调用的函数,与异步操作关联的任何附加数据都会被传入这个完成函数。第二个参数则是Promise被拒绝时要调用的函数,与完成函数相似,拒绝函数会被传入与拒绝相关联的任何附加数据。

如下:

let promise = readFile("example.txt");
promise.then(function(contents) {
    // 完成
    console.log(contents);
}, function(err) {
    // 拒绝
    console.error(err.message);
});

catch()方法

其行为等同于只传递拒绝处理函数给 then() 。例如,以下的 catch() 与then()调用是功能等效的。

promise.catch(function(err) {
    // 拒绝
    console.error(err.message);
});
// 等同于:
promise.then(null, function(err) {
    // 拒绝
    console.error(err.message);
});

创建一个未决的Promise()

新的 Promise 使用 Promise 构造器来创建。此构造器接受单个参数:一个被称为执行器(
executor )的函数,包含初始化 Promise 的代码。该执行器会被传递两个名为 resolve()
与 reject() 的函数作为参数。 resolve() 函数在执行器成功结束时被调用,用于示意该
Promise 已经准备好被决议( resolved ),而 reject() 函数则表明执行器的操作已失败。

// Node.js 范例
let fs = require("fs");
function readFile(filename) {
    return new Promise(function(resolve, reject) {
        // 触发异步操作
        fs.readFile(filename, { encoding: "utf8" }, function(err, contents) {
            // 检查错误
            if (err) {
                reject(err);
                return;
            }
            // 读取成功
            resolve(contents);
        });
    });
}
let promise = readFile("example.txt");
// 同时监听完成与拒绝
promise.then(function(contents) {
    // 完成
    console.log(contents);
}, function(err) {
    // 拒绝
    console.error(err.message);
});

//等同于
promise.then((contents)=>{
    // 完成
    console.log(contents);
}).catch((err)=>{
    // 拒绝
    console.error(err.message);
});

以上是一个基本的Promise模型,then()方法执行fs.readFile()读取文件成功后的回调函数,catch()方法执行读取文件失败后的错误回调

创建已决的 Promise

即创建的时候就决定了结果,Promise.resolve() 方法接受单个参数并会返回一个处于完成态的 Promise 。

let promise = Promise.resolve(42);
promise.then(function(value) {
    console.log(value); // 42
});

Promise.reject() 方法来创建一个已拒绝的 Promise 。此方法像Promise.resolve() 一样工作,区别是被创建的 Promise 处于拒绝态

let promise = Promise.reject(42);
promise.catch(function(value) {
    console.log(value); // 42
});

非 Promise 的 Thenable

Promise.resolve() 与 Promise.reject() 都能接受非 Promise 的 thenable 作为参数。当传
入了非 Promise 的 thenable 时,这些方法会创建一个新的 Promise ,此 Promise 会在
then() 函数之后被调用。

当一个对象拥有一个能接受 resolve 与 reject 参数的 then() 方法,该对象就会被认为是
一个非 Promise 的 thenable ,就像这样:

let thenable = {
    then: function(resolve, reject) {
        resolve(42);
    }
};

此例中的 thenable 对象,除了 then() 方法之外没有任何与 Promise 相关的特征。你可以
调用 Promise.resolve() 来将 thenable 转换为一个已完成的 Promise :

let thenable = {
    then: function(resolve, reject) {
        resolve(42);
    }
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});

链式Promise

每次对 then() 或 catch() 的调用实际上创建并返回了另一个 Promise ,仅当前一个
Promise 被完成或拒绝时,后一个 Promise 才会被决议。

因此我们可以进行链式调用

let p1 = new Promise(function(resolve, reject) {
    resolve(42);
});
p1.then(function(value) {
    console.log(value);
    return value+1
}).then(function(val) {
    console.log(val);
}).catch((err)=>{
    
});

以上中第一个then方法会作为第二个then方法的入参,把then方法的返回值一直传递下去,Promise 链允许你捕获前一个 Promise 的完成或拒绝处理函数中发生的错误。因此只要有某个then方法出现错误,或者直接失败就会调用catch方法

在 Promise 链中返回 Promise

从完成或拒绝处理函数中返回一个基本类型值,能够在Promise之间传递数据,但若你返回的是一个对象呢?若该对象是一个 Promise ,那么需要采取一个额外步骤来决定如何处理。

let p1 = new Promise(function(resolve, reject) {
    resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
    resolve(43);
});
p1.then(function(value) {
    // 第一个完成处理函数
    console.log(value); // 42
    return p2;
}).then(function(value) {
    // 第二个完成处理函数
    console.log(value); // 43
})catch((err)=>{
    
});

在此代码中, p1 安排了一个决议 42 的作业, p1 的完成处理函数返回了一个已处于决议态的 Promise : p2 。由于 p2 已被完成,第二个完成处理函数就被调用了。而若p2被拒绝,会调用拒绝处理函数(如果存在的话),而不调用第二个完成处理函数。

Promise.all() 方法

Promise.all() 方法接收单个可迭代对象(如数组)作为参数,并返回一个 Promise 。这个可迭代对象的元素都是 Promise ,只有在它们都完成后,所返回的 Promise 才会被完成。

let p1 = new Promise(function(resolve, reject) {
    resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
    resolve(43);
});
let p3 = new Promise(function(resolve, reject) {
    resolve(44);
});
let p4 = Promise.all([p1, p2, p3]);
p4.then(function(value) {
    console.log(Array.isArray(value)); // true
    console.log(value[0]); // 42
    console.log(value[1]); // 43
    console.log(value[2]); // 44
});

此处前面的每个 Promise 都用一个数值进行了决议,对 Promise.all() 的调用创建了新的Promise p4 ,在 p1 、 p2 与 p3 都被完成后, p4 最终会也被完成。传递给 p4 的完成处理函数的结果是一个包含每个决议值( 42 、 43 与 44 )的数组,这些值的存储顺序保持了待决议的 Promise 的顺序(与完成的先后顺序无关),因此你可以将结果匹配到每个
Promise 。

若传递给 Promise.all() 的任意 Promise 被拒绝了,那么方法所返回的 Promise 就会立刻被拒绝,而不必等待其他的 Promise 结束

也就是说只要数组里面的promise有一个执行失败,Promise.all()就会立马执行失败决议,调用catch()方法

Promise.race() 方法

Promise.race()跟Promise.all()方法入参都一样,只是Promise.race()是只要有一个Promise执行成功决议,Promise.race()就执行成功决议

这两种方法十分类似于数组的every()方法和some()方法

继承 Promise

正像其他内置类型,你可将一个Promise用作派生类的基类。这允许你自定义变异的Promise,在内置Promise的基础上扩展功能。例如,假设你想创建一个可以使用success()与failure()方法的Promise,对常规的then()与catch()方法进行扩展,可以像下面这样创建该 Promise 类型:

class MyPromise extends Promise {
    // 使用默认构造器
    success(resolve, reject) {
        return this.then(resolve, reject);
    }
    failure(reject) {
        return this.catch(reject);
    }
}
let promise = new MyPromise(function(resolve, reject) {
    resolve(42);
});
promise.success(function(value) {
console.log(value); // 42
}).failure(function(value) {
console.log(value);
});

可以封装自己的Promise哦!

总结

Promise 被设计用于改善 JS 中的异步编程,与事件及回调函数对比,在异步操作方面为你提供了更多的控制权与组合性。 Promise 调度被添加到 JS 引擎作业队列,以便稍后执行。不过此处有另一个作业队列追踪着Promise的完成与拒绝处理函数,以确保适当的执行。

Promise 具有三种状态:挂起、已完成、已拒绝。一个Promise起始于挂起态,并在成功时转为完成态,或在失败时转为拒绝态。在这两种情况下,处理函数都能被添加以表明Promise何时被解决。then()方法允许你绑定完成处理函数与拒绝处理函数,而 catch()方法则只允许你绑定拒绝处理函数。

你能用多种方式将多个 Promise 串联在一起,并在它们之间传递信息。每个对 then() 的调用都创建并返回了一个新的 Promise ,在前一个 Promise 被决议时,新Promise也会被决议。Promise链可被用于触发对一系列异步事件的响应。你还能使用 Promise.race() 与Promise.all() 来监视多个 Promise 的进程,并进行相应的响应。

组合使用生成器与 Promise 会让异步任务运行得更容易,这是由于Promise提供了异步操作可返回的一个通用接口。这样你就能使用生成器与 yield 运算符来等待异步响应,并作出适当的应答。