ES6--Promise

Promise的引出

封装node方法,传入文件路径,可以读取文件并将内容返回

 1 const fs = require('fs')
 2 const path = require('path')
 3 
 4 //封装函数
 5 function getFileByPath(fpath) {
 6     fs.readFile(fpath, 'utf-8', (err, dataStr) => {
 7         if (err) throw err
 8         return dataStr
 9     })
10 
11 }
12 
13 //readFile为异步处理函数,放到队列里继续运行主程序,直接跳到第10行代码。函数执行发现没有return,默认返回undefined,所以上述函数运行结果都是undefined
14 
15 var data = getFileByPath(path.join(__dirname, './files/1.txt'))
16 console.log(data)  //undefined

所以上述方法不能拿到文件的数据,进一步改造函数,使用回调:

function getFileByPath(fpath, callback) {
    fs.readFile(fpath, 'utf-8', (err, dataStr) => {
        if (err) throw err
        callback(dataStr)
    })
}

getFileByPath(path.join(__dirname, './files/1.txt'), (data) => {
    console.log(data)
})

上面函数,如果传入的文件类型不正确就不能执行后面的代码,可以将文件路径错误时的处理再进一步优化,将错误结果也进行返回

//将错误结果也进行返回,并规定成功的结果位于回调函数的第二个位置,失败的结果位于回调函数的第一个位置
function getFileByPath(fpath, callback) {
    fs.readFile(fpath, 'utf-8', (err, dataStr) => {
        if (err) return callback(err)
        callback(null, dataStr)
    })
}

getFileByPath(path.join(__dirname, './files/1.txt'), (err, data) => {
    if (err) {
        console.log(err.message)
    } else {
        console.log(data)
    }
})

回调函数将成功与失败的结果都在一起处理进行优化,将两种结果分成两个回调函数

function getFileByPath(fpath, succCb, errCb) {
    fs.readFile(fpath, 'utf-8', (err, dataStr) => {
        if (err) return errCb(err)
        succCb(dataStr)
    })
}

getFileByPath(path.join(__dirname, './files/11.txt'), data => {
    console.log('成功的结果为' + data)
}, err => {
    console.log('失败的结果' + err.message)
})

如果有一个需求为,先读取文件1,再读取文件2,最后再读取文件3,调用函数如下

getFileByPath(path.join(__dirname, './files/1.txt'), data => {
    console.log(data)

    getFileByPath(path.join(__dirname, './files/2.txt'), data => {
        console.log(data)

        getFileByPath(path.join(__dirname, './files/3.txt'), data => {
            console.log(data)
        })
    })
})

这种函数作为参数层层嵌套称为回调地狱

使用es6中的Promise来解决回调地狱的问题(使用Promise并不会减少代码量,只是将多层的嵌套改成了代码串联的形式)

Promise概念

1、promise是一个构造函数,可以通过new创建实例

2、在Promise上有两个函数,resolve(成功之后的回调函数)和reject(失败之后的回调函数)

3、在Promise构造函数的prototype属性上,有then方法。通过Promise创造的实例都可以使用then方法

4、每一个Promise实例表示一个异步操作

5、通过then方法指定回调函数时,成功的回调函数必须传,失败的回调函数可以不传

6、Promise实例只要被创建,就会立即执行里面的异步方法

var promise = new Promise(function () {
    fs.readFile(path.join(__dirname, './files/2.txt'), 'utf-8', (err, dataStr) => {
        if (err) throw err;
        console.log(dataStr)
    })
})
//node运行后,会打印出文件内容,说明异步函数被执行
//也就是说,在使用new关键字后,除了得到Promise实例,还会立即调用我们为Promise构造函数传递的function,执行function中的异步操作代码

如果不想使函数在调用时才执行,可以将Promise包裹在另一个函数中

function getFileByPath(fpath) {
    var promise = new Promise(function () {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) throw err;
            console.log(dataStr)
        })
    })
}

getFileByPath(path.join(__dirname, './files/2.txt'))

 如果想获得文件读取的结果,直接在readFile中return会像之前函数封装一样,因为异步操作并不能拿到结果,所以需要借助回调函数。Promise为我们提供了成功回调resolve与失败回调reject,在new Promise的函数参数中,传入resolve与reject,代码如下

function getFileByPath(fpath) {
    var promise = new Promise(function (resolve, reject) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return reject(err);
            resolve(dataStr)
        })
    })
}
//resolve与reject为形参

通过then方法可以指定成功和失败的回调,需要通过Promise的实例调用方法,所以需要将上面函数中的promise返回

function getFileByPath(fpath) {
    var promise = new Promise(function (resolve, reject) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return reject(err);
            resolve(dataStr)
        })
    })
    return promise
}

通过下面方法调用

var p = getFileByPath(path.join(__dirname, './files/2.txt'))

p.then(data => {
    console.log('读取成功' + data)
}, err => {
    console.log('读取失败' + err.message)
})

使用上面的Promise函数完成“先读取文件1,再读取文件2,最后再读取文件3”需求

getFileByPath(path.join(__dirname, './files/1.txt')).then(data => {
    console.log('文件1读取成功,开始读取文件2数据')
    getFileByPath(path.join(__dirname, './files/2.txt')).then(data => {
        console.log('文件2读取成功,开始读取文件3数据')
        getFileByPath(path.join(__dirname, './files/3.txt')).then(data => {
            console.log('文件3读取成功')
        })
    })
})

可以看到上面的函数依然是层层嵌套,因为这并不是Promise正确的打开方式。优化代码如下:

Promise的链式调用

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('aaa')
    }, 1000)
}).then(res => {
    console.log(`${res},第一层代码`)
    return new Promise(resolve => {
        resolve(res + '111')
    })
}).then(res => {
    console.log(`${res},第二层代码`)
    return new Promise(resolve => {
        resolve(res + '222')
    })
}).then(res => {
    console.log(`${res},第三次代码`)
})

可这样简写

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('aaa')
    }, 1000)
}).then(res => {
    console.log(`${res},第一层代码`)
    return Promise.resolve(res + '111')
}).then(res => {
    console.log(`${res},第二层代码`)
    return Promise.resolve(res + '222')
}).then(res => {
    console.log(`${res},第三次代码`)
})

更加简洁的方式

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('aaa')
    }, 1000)
}).then(res => {
    console.log(`${res},第一层代码`)
    return res + '111'
}).then(res => {
    console.log(`${res},第二层代码`)
    return res + '222'
}).then(res => {
    console.log(`${res},第三次代码`)
})

Promise捕获异常

上面代码中并没有对异常进行处理。当其中一个路径不正确时,例如将1.txt替换成11.txt,其中11.txt是不存在的

getFileByPath(path.join(__dirname, './files/11.txt')).then(data => {
    console.log('文件1读取成功,开始读取文件2数据' + data)

    return getFileByPath(path.join(__dirname, './files/2.txt'))
}).then(data => {
    console.log('文件2读取成功,开始读取文件3数据' + data)

    return getFileByPath(path.join(__dirname, './files/3.txt'))
}).then(data => {
    console.log('文件3读取成功' + data)
})

console.log('OK')

上面代码执行后,会先打印出'OK'再报错。因为报错所在的函数是异步操作。

1、如果前面的Promise执行失败后不影响后面的Promise执行,这样做:

getFileByPath(path.join(__dirname, './files/11.txt')).then(data => {
    console.log('文件1读取成功,开始读取文件2数据' + data)

    return getFileByPath(path.join(__dirname, './files/2.txt'))
}, err => {
    console.log('这是失败的信息' + err.message)
    //需要return一个新的Promise不影响后面的.then
    return getFileByPath(path.join(__dirname, './files/2.txt'))
}).then(data => {
    console.log('文件2读取成功,开始读取文件3数据' + data)

    return getFileByPath(path.join(__dirname, './files/3.txt'))
}).then(data => {
    console.log('文件3读取成功' + data)
})

2、如果后续的Promise执行依赖于前面的Promise结果,前面的Promise失败后,则立即终止所有的Promise执行,通过.catch

getFileByPath(path.join(__dirname, './files/11.txt')).then(data => {
    console.log('文件1读取成功,开始读取文件2数据' + data)

    return getFileByPath(path.join(__dirname, './files/2.txt'))
}).then(data => {
    console.log('文件2读取成功,开始读取文件3数据' + data)

    return getFileByPath(path.join(__dirname, './files/3.txt'))
}).then(data => {
    console.log('文件3读取成功' + data)
}).catch(err => {   //如果前面有任何的Promise执行失败,会立即终止Promise执行,并进入catch中
    console.log('这是错误处理'+err.message)
})

Promise的三种状态

  • pending :等待状态,比如正在进行网络请求或者定时器没有到时间
  • fulfill : 满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
  • reject : 拒绝状态,当我们主动回调了reject时就处于该状态,并且会回调.catch()

Promise.all

需求:在两个请求都结束后进行xxx操作

普通回调

let isResult1 = false
let isResult2 = false
$.ajax({
    url: 'url1',
    success(data) {
        isResult1 = true
        handleResult()
    }
})
$.ajax({
    url: 'url2',
    success(data) {
        isResult2 = true
        handleResult()
    }
})

function handleResult() {
    if (isResult1 && isResult2) {
        //执行代码
    }
}

使用all

Promise.all([
    new Promise((resolve, reject) => {
        $.ajax({
            url: 'url1',
            success(data) {
                resolve(data)
            }
        })
    }),
    new Promise((resolve, reject) => {
        $.ajax({
            url: 'url2',
            success(data) {
                resolve(data)
            }
        })
    })
]).then(results => {
    results[0]  //url1的结果
    results[1]  //url2的结果
})
原文地址:https://www.cnblogs.com/lianglanlan/p/9857288.html