精簡的非同步Promise實現

王衛 mathew發表於2018-11-02

Promise在web前端的開發工程師中使用的非常頻繁,通過then使同步的程式設計思路來進行非同步呼叫,使得非同步編碼變得非常的簡單。iOS也有PromiseKit這樣的開源庫可以使用,不過自己實現一個使用起來會更方便和貼近。


web端的Promise使用

//第一個非同步任務
    function run_a(){
        return new Promise(function(resolve, reject){
            //假設已經進行了非同步操作,並且獲得了資料
            resolve("step1");
        });
    }
    //第二個非同步任務
    function run_b(data_a){
        return new Promise(function(resolve, reject){
            //假設已經進行了非同步操作,並且獲得了資料
            console.log(data_a);
            resolve("step2");
        });
    }
    //第三個非同步任務
    function run_c(data_b){
        return new Promise(function(resolve, reject){
            //假設已經進行了非同步操作,並且獲得了資料
            console.log(data_b);
            resolve("step3");
        });
    }

    //連續呼叫
    run_a().then(function(data){
        return run_b(data);
    }).then(function(data){
        return run_c(data);
    }).then(function(data){
        console.log(data);
    });

    /*執行結果
      step1
      step2
      step3
    */
複製程式碼

參考上面的例子,對Promise稍微有些瞭解的應該都知道Promise包裝了一個非同步呼叫並生成一個Promise例項,當非同步呼叫返回的時候根據呼叫的結果分別呼叫例項化時傳入的resolvereject方法,這個時候then就會接收到對應的資料,這個是Promise最基本使用,當然真正的Promise還包含了很多其它的使用,這裡我就使用OC實現最基本的非同步呼叫then功能。

Objective C 的實現

實現程式碼放在github上,只有一個類檔案可以隨意的copy到工程中:github.com/MathewWang/…

如何使用

// Resolve Example
  WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          resolve(@"aaa");
      });
  }).then(^(NSString *result){
      NSLog(@"###promise the result is 111: %@", result);

      return WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
              resolve([result stringByAppendingString:@"bbb"]);
          });
      });
  }, nil).then(^(NSString *result){
      NSLog(@"###promise the result is 222: %@", result);

      return [WWPromise empty];
  }, nil);
  
// Reject Example
   WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          reject(@"aaa");
      });
  }).then(nil, ^(NSString *result){
      NSLog(@"###promise the result is 111: %@", result);

      return [WWPromise empty];
  });
  
複製程式碼

參照上面的例子,在新增then的時候,對於resolvereject的回撥函式,如果不需要直接傳入nil即可,不需要返回引數的then需要返回一個**[WWPromise empty]。 ###One More 因為Promise在例項化的時候就開始執行非同步呼叫了,這點和RAC的冷訊號不一樣(冷訊號是訂閱時觸發非同步呼叫的),因此我們可以將Promise**再進行一次封裝使得對於OC的開發更加友好,這也是我們業務中真正使用的樣子: 實現一個抽象類“AbstractPromiseExecution”

typedef void(^ExecutionSuccess)(id result);
typedef void(^ExecutionFailed)(NSError *error);

@interface AbstractPromiseExecution()

@property (nonatomic, strong) ExecutionSuccess success;
@property (nonatomic, strong) ExecutionFailed failed;


@end

@implementation AbstractPromiseExecution

- (WWPromise *(^)(id))promise{
    return ^(id bizDatas){
        return WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
            //設定success
            self.success = ^(id result) {
                resolve(result);
            };
            //設定failed
            self.failed = ^(NSError *error) {
                reject(error);
            };
            [self startExecute:bizDatas];
        });
    };
}

- (void)startExecute:(id) bizDatas{
    //override by subclass
}
複製程式碼

從上面的抽象類很明顯的看出來,- promise方法會返回一個Promise例項,將業務的successfailed回撥賦值,同時會執行模板方法**- (void)startExecute:(id) bizDatas將需要傳遞的業務資料bizDatas傳遞下去,因此業務類只要繼承AbstractPromiseExecution然後複寫- (void)startExecute:(id) bizDatas**方法,在其中新增自己的業務邏輯,成功後呼叫 success 或者 failed,事例程式碼如下:

//子類 1
@interface FetchUserGroupInfoExecution : AbstractPromiseExecution

@end

@implementation FetchUserGroupInfoExecution

- (void)startExecute:(id) bizDatas{
     //做非同步請求
    [UserService requestUserGroup:^(id result){
          strongSelf.success(result);
    } error:^(NSError *errorInfo){
          strongSelf.failed(errorInfo);
    }];
}

@end

//子類 2
@interface CheckGroupAuthrizationExecution : AbstractPromiseExecution

@end

@implementation CheckGroupAuthrizationExecution

- (void)startExecute:(id) bizDatas{
     //做非同步請求
    [UserGroupService checkGroupAuthrization:^(id result){
          strongSelf.success(result);
    } error:^(NSError *errorInfo){
          strongSelf.failed(errorInfo);
    }];
}

@end

複製程式碼

這兩個子類是先獲取使用者的組資訊,然後在獲取這個組的許可權資訊,在業務使用時候如下:

FetchUserGroupInfoExecution *fetchExecution = [FetchUserGroupInfoExecution new];

CheckGroupAuthrizationExecution *checkExecution = [CheckGroupAuthrizationExecution new];

fetchExecution.promise(@{@"userId":@123})
.then(^(id res){
  //Do business
  return checkExecution.promise(res);
},^(NSError *error){
  //Do error handling
}
).then(^(id res){
  //Do business
  return [WWPromise empty];
},^(NSError *error){
  //Do error handling
}
);

複製程式碼

通過上面這樣的簡單封裝,以後的業務擴充套件也是非常的容易,編寫也很簡單,子類只要關心傳進來的資料做與自己相關的業務處理即可。

相關文章