> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# nodejs promise介绍
- URL: https://blog.hacking.pub/2016/10/26/nodejs-promisejie-shao/
- Published: 2016-10-26T16:17:59.000Z
- Updated: 2016-10-27T16:25:54.000Z
- Author: chainhelen
- Tags: nodejs, promise, 异步

### promise 概念

ES6 原生支持Promise  
Promise是一个构造函数，包含all，reject，resolve等方法。一个Promise用来传递异步操作的消息，它代表了未来才会知道结果的事件。  
本质上是一种异步编程的抽象，是返回值或者抛出exception的代理对象。它有一个then方法，任何人都可以访问then来消费这个返回值或exception的。

### 三种状态

promise只有三种状态，并且状态转移只能是pending -> fulfiled 或者pending -> rejected  
**pending**: 待定状态，Pormise对象刚被初始化的状态  
**fulfilled**: 完成状态，承诺被完成的状态  
**rejected**: 拒绝状态，承诺完成失败的状态  
**relosve和reject**: resolve() 和 reject()是 Promise的两个闭包函数。

通常，读取一个文本，我们会这么写。

```js
"use strict"
const fs = require('fs');
fs.readFile('test.txt', (err, data) => {
    if(err){
        console.error(err);
        return;
    }
    console.log(data);
});

```

接下来我们来封装类似promise风格的函数，如下：

```js
"use strict"
const fs = require('fs');
let proReadFile = (filename) => {
    return new Promise( (resolve, reject) => { 
        fs.readFile(filename, (err, data) => {
            if(err){
                reject(err);
                return;
            }
            resolve(data);
        });
    });
};

proReadFile('text.txt').then(console.log).catch(console.error);

```

这里把**fs.readFile**封装成一个**promise**如果读入正常就利用then把数据交给**console.log**函数消费，如果失败就利用catch把err交给**console.error**消费

#### 分析代码

[源码](https://github.com/ygm125/promise/blob/master/promise.js?ref=blog.hacking.pub)  
一个基本的MyPromise

```
//函数构造器
function MyPromise(exe) {

    //三种状态，默认"pending"
    this.status = "pending";
    //如果状态是rejected, reason 的值非空，带给消费者消费
    this.reason = null;
    //如果状态是fulfilled, result 的值非空，带给消费者消费
    this.result = null;

    //resolve回调函数数组，当exe执行器执行完，触发该函数数组，而后修改状态为fulfilled    
    this.resolveArr = [];
    //reject回调函数数组，当exe执行器执行完，触发该函数数组，而后修改状态为rejected
    this.rejectArr  = [];
    
    //保证各个函数里面的变量是该对象的属性
    let self = this;
    
    //promise留给外部的闭包函数，依次触发resolveArr里面的函数
    let resolve = () => {
        if("pending" != self.status) {
            return ;
        }
        //setTimeout异步的目的，是为了让then的方法先执行，将消费函数先注册上
        //否则resolve执行完，then里面的函数并不会执行
        setTimeout( () => {
            let fn = null;
            let result = self.result;
            while(fn = self.resolveArr.shift()) {
                result = fn(result);
            }
            self.result = result;
            self.status = "fulfilled";
        });
    }
    //同理如上
    let reject  = () => {
        if("pending" != self.status) {
            return ;
        }
        setTimeout( ()=> {
            let fn = null;
            let reason = self.reason;
            while(fn = self.resolveArr.shift()) {
               reason = fn(reason);
            }
        
            self.reason = reason;
            self.status = "rejected";
        });
    }
    exe(resolve, reject);
}

```

再来看看then链式结构

```
MyPromise.prototype.then = (onFulfilled, onRejeceted) => {
    let self = this;
    //最终返回promise对象，保证thenable
    return new MyPromise( (resolve, reject) => {
        //当promise.status 为"resolved", 成功执行下一个链式
        function cb(result) {
            let ret = ("function" == typeof onFulfilled && onFulfilled(result)) || result;
            if(ret["then"]) {
                ret.then( (result)=>{
                    resolve(result);
                }, (reason)=>{
                    reject(reason);
                });
            } else {
                resolve(ret);
            }
        }

        //当promise.status 为"rejected", reject就把reason传递给执行下一个链式
        function errcb(reason) {
            let ret = ("function" == typeof onRejeceted && onRejeceted(reason)) || reason;
            reject(reason);
        }

        if("pending" == self.status) {
            self.resolveArr.push(cb);
            self.rejectArr.push(errcb);
        } else if ("rejected" == self.status) {
            errcb(self.reason);
        } else if ("resolved" == self.status) {
            cb(self.result);
        }
    });
}

```

现在看看**resolve**函数，**reject**同理：

- MyPromise的构造函数，new出新的promise对象P1，会立即执行使用者传递的执行器exe，当exe运行成功，便会触发resolve函数，并把数据交给resolve函数。但由于resolve里面的setTimouet不会立即触发resolveArr里面函数。这时候resolveArr里面还是空的（假设exe里面从执行到触发resolve都是同步执行的）。
- resolveArr里面的函数是怎么来的，在then里面构造promise对象P2时，从then里面获取的: self.resolveArr.push(cb)。这时候，只是把cb不停地塞进resolveArr里面，但不会立即执行。
- 直到P1的resolve里面的异步setTimeout超时，匿名函数执行。不停地弹出resolveArr里面的函数，这里面的函数全是cb, cb的参数是P1的result。
- 就拿上叙第一例说明，cb和console.log是什么关系。其实简单一点，console.log就是onFulfilled，会被cb函数在其中间部分调用。先不看if(ret\["then"\])，先看else，就是把onFulfilled里面结果或者说是P2结果赋值给P2的resolve执行，又是promise的核心。

catch 是 then 的语法糖，类似then(null, reject);