解决异步编程多层嵌套问题 ==》Promise容器

2,435 阅读3分钟

场景:
  js中多个异步调用(接口,读取文件)时没有顺序,若业务现在要求有顺序的调用,就只能嵌套回调,如果嵌套回调3个以下代码量还不是很多,还可以凑乎,超过3个后重复代码多,可维护性差,代码丑陋ugly,就造成了callback hell,这也就是:回调地狱。如下图所示。

解决方案

  对此,Es6出了promise解决此问题。而且,jquery也支持promise功能,node中的mongoose也支持promise功能。
   promise容器概念: pending正在进行后有2种状态,要么成功变成resolved,要么失败变成rejected,如图所示:

promise的API调用: Promise 是一个构造函数,创建 Promise 容器,需要 new Promise(function(resolve,reject){异步操作}) ,如下图所示

  1. 在 promise 对象.then(fun1,fun2)方法中,resolve(data)执行fun1方法,reject(err)执行的是fun2方法

  2. 如果在 then 的 fun1 方法中 return 结果,可以在后面的 then 中的 fun1 中接收到

    • 当 return 123,后面方法的参数就接收到 123
    • 当 return ‘hello’,后面方法的参数就接收到 ‘hello’
    • 没有 return ,后面方法的参数就接收到 undefined

    不过一般是不会 return 数字或者字符串,真正有用的是return 一个 Promise 对象

  3. 当then的 fun1 中 return 一个promise 对象1,下个链式的 then 中的 fun1 就是这个 promise 对象1的 resolve(data),在 then 的 fun2 中return 一个 promise 对象2,下个链式的 then 中的 fun2 就是这个 promise 对象2的reject(data)

var fs = require('fs')

var p1 = new Promise(function(resolve,reject){
    fs.readFile('./txt/a.txt',function(err,data){
        if(err){
            console.log("run p1 reject")
            reject(err)
        }else{
            console.log("run p1 resole")
            resole(data.toString())
        }
    })
})

var p2 = new Promise(function(resole,reject){
    fs.readFile('./txt/b.txt',function(err,data){
        if(err){
            console.log("run p2 reject")
            reject(err)
        }else{
            console.log("run p2 resole")
            resole(data.toString())
        }
    })
})

p1.then(function(data){
// 对应的是p1的resolve()
//1. return 123
//2. return 'hello'
//3 不return
//4. return p2 (p2是个promise对象)
},function(data){
// 对应的是p1的reject()    
})
.then(function(data){
// 1. console.log(‘data ==’+data) ===> data==123
// 2. console.log(‘data ==’+data) ===> data==hello
// 3. console.log(‘data ==’+data) ===> data==undefined
// 4. console.log(‘data ==’+data) ===> data==p2中resolve(d)的实参 d,
当前该函数就是p2中resolve(d)的定义
})

如下图:

例子:读取文件a的内容后再读取文件b的内容,然后再获取文件c的内容
1. 如果不使用 promise容易,一般的方法是回调嵌套,即回调地狱

var fs = require('fs')

fs.readFile('./txt/a.txt',function(err,data){
    if(err){
        console.log('文件a读取失败')
    }else{
        console.log(data.toString())

        fs.readFile('./txt/b.txt',function(err,data){
            if(err){
                console.log('文件b读取失败')
            }else{
                console.log(data.toString())
        
                fs.readFile('./txt/c.txt',function(err,data){
                    if(err){
                        console.log('文件b读取失败')
                    }else{
                        console.log(data.toString())
                    }
        
                })
            }
        })
    }
})

2. 使用 promise 容器,未封装读取文件

var fs = require('fs')

var p1 = new Promise(function(resole,reject){
    fs.readFile('./txt/a.txt',function(err,data){
        if(err){
            console.log("run p1 reject")
            reject(err)
        }else{
            console.log("run p1 resole")
            resole(data.toString())
        }
    })
})

var p2 = new Promise(function(resole,reject){
    fs.readFile('./txt/b.txt',function(err,data){
        if(err){
            console.log("run p2 reject")
            reject(err)
        }else{
            console.log("run p2 resole")
            resole(data.toString())
        }
    })
})

var p3 = new Promise(function(resole,reject){
    fs.readFile('./txt/c.txt',function(err,data){
        if(err){
            console.log("run p3 reject")
            reject(err)
        }else{
            console.log("run p3 resole")
            resole(data.toString())
        }
    })
})

p1.then(function(data){
    console.log('p1 then resolved',data)
    return p2
})
.then(function(data){
    console.log('p2 then resolve',data)
    return p3
})
.then(function(data){
    console.log('p3 then resolve',data)
})

3. 使用 promise 容器,对读取文件进行封装

var fs = require('fs')

function pReadFile(url){
   return new Promise(function(resolve,reject){
        fs.readFile(url,function(err,data){
            if(err){
                reject(err)
            }else{
                resolve(data.toString())
            }
        })
    })
}

var pRF = pReadFile('./txt/a.txt')

pRF.then(function(data){
    console.log('run  resolve fun',data)
    return pReadFile('../txt/b.txt')
},function(err){
    console.log('run  reject fun','a文件读取失败')
    return pReadFile('../txt/b.txt')
})
.then(function(data){
    console.log(data)
    return pReadFile('./txt/c.txt')
},function(err){
    console.log('run  reject fun','b文件读取失败')
    return pReadFile('./txt/c.txt')
})
.then(function(data){
    console.log('run resolve fun',data)
},function(err){
    console.log('run  reject fun','c文件读取失败')
})